diff --git a/actions/ql/lib/CHANGELOG.md b/actions/ql/lib/CHANGELOG.md index 7a61a60c379..2b79e89d6d1 100644 --- a/actions/ql/lib/CHANGELOG.md +++ b/actions/ql/lib/CHANGELOG.md @@ -2,7 +2,7 @@ ### Minor Analysis Improvements -* The GitHub Actions analysis now recognizes more Bash regex checks that restrict a value to alphanumeric characters, include regexes like `^[0-9a-zA-Z]{40}([0-9a-zA-Z]{24})?$` which check for a sha1 or sha256 hash. This may reduce false positive results where command output is validated with grouped or optional alphanumeric patterns before being used. +* The GitHub Actions analysis now recognizes more Bash regex checks that restrict a value to alphanumeric characters, including regexes like `^[0-9a-zA-Z]{40}([0-9a-zA-Z]{24})?$` which check for a SHA-1 or SHA-256 hash. This may reduce false positive results where command output is validated with grouped or optional alphanumeric patterns before being used. ## 0.4.36 diff --git a/actions/ql/lib/change-notes/released/0.4.37.md b/actions/ql/lib/change-notes/released/0.4.37.md index 4809796b3ab..f1e071571fd 100644 --- a/actions/ql/lib/change-notes/released/0.4.37.md +++ b/actions/ql/lib/change-notes/released/0.4.37.md @@ -2,4 +2,4 @@ ### Minor Analysis Improvements -* The GitHub Actions analysis now recognizes more Bash regex checks that restrict a value to alphanumeric characters, include regexes like `^[0-9a-zA-Z]{40}([0-9a-zA-Z]{24})?$` which check for a sha1 or sha256 hash. This may reduce false positive results where command output is validated with grouped or optional alphanumeric patterns before being used. +* The GitHub Actions analysis now recognizes more Bash regex checks that restrict a value to alphanumeric characters, including regexes like `^[0-9a-zA-Z]{40}([0-9a-zA-Z]{24})?$` which check for a SHA-1 or SHA-256 hash. This may reduce false positive results where command output is validated with grouped or optional alphanumeric patterns before being used. diff --git a/actions/ql/src/CHANGELOG.md b/actions/ql/src/CHANGELOG.md index c37cd20761b..cc99d741c50 100644 --- a/actions/ql/src/CHANGELOG.md +++ b/actions/ql/src/CHANGELOG.md @@ -15,7 +15,7 @@ ### Bug Fixes -* Adjusted (minor) help file descriptions for queries: `actions/untrusted-checkout/critical`, `actions/untrusted-checkout/high`, `actions/untrusted-checkout/medium`. Clarified wording on in minor point, added one more listed resource and added one more recommendation for things to check. +* Adjusted (minor) help file descriptions for queries: `actions/untrusted-checkout/critical`, `actions/untrusted-checkout/high`, `actions/untrusted-checkout/medium`. Clarified wording on a minor point, added one more listed resource and added one more recommendation for things to check. ## 0.6.28 diff --git a/actions/ql/src/change-notes/released/0.6.29.md b/actions/ql/src/change-notes/released/0.6.29.md index 82ca8174954..70c69f82399 100644 --- a/actions/ql/src/change-notes/released/0.6.29.md +++ b/actions/ql/src/change-notes/released/0.6.29.md @@ -15,4 +15,4 @@ ### Bug Fixes -* Adjusted (minor) help file descriptions for queries: `actions/untrusted-checkout/critical`, `actions/untrusted-checkout/high`, `actions/untrusted-checkout/medium`. Clarified wording on in minor point, added one more listed resource and added one more recommendation for things to check. +* Adjusted (minor) help file descriptions for queries: `actions/untrusted-checkout/critical`, `actions/untrusted-checkout/high`, `actions/untrusted-checkout/medium`. Clarified wording on a minor point, added one more listed resource and added one more recommendation for things to check. diff --git a/csharp/downgrades/d13c4c187d7318fd2b8f35c7e8d7f4dc26be68b1/old.dbscheme b/csharp/downgrades/d13c4c187d7318fd2b8f35c7e8d7f4dc26be68b1/old.dbscheme new file mode 100644 index 00000000000..d13c4c187d7 --- /dev/null +++ b/csharp/downgrades/d13c4c187d7318fd2b8f35c7e8d7f4dc26be68b1/old.dbscheme @@ -0,0 +1,1511 @@ +/* This is a dummy line to alter the dbscheme, so we can make a database upgrade + * without actually changing any of the dbscheme predicates. It contains a date + * to allow for such updates in the future as well. + * + * 2021-07-14 + * + * DO NOT remove this comment carelessly, since it can revert the dbscheme back to a + * previously seen state (matching a previously seen SHA), which would make the upgrade + * mechanism not work properly. + */ + +/** + * An invocation of the compiler. Note that more than one file may be + * compiled per invocation. For example, this command compiles three + * source files: + * + * csc f1.cs f2.cs f3.cs + * + * The `id` simply identifies the invocation, while `cwd` is the working + * directory from which the compiler was invoked. + */ +compilations( + unique int id : @compilation, + string cwd : string ref +); + +compilation_info( + int id : @compilation ref, + string info_key: string ref, + string info_value: string ref +) + +/** + * The arguments that were passed to the extractor for a compiler + * invocation. If `id` is for the compiler invocation + * + * csc f1.cs f2.cs f3.cs + * + * then typically there will be rows for + * + * num | arg + * --- | --- + * 0 | --compiler + * 1 | *path to compiler* + * 2 | f1.cs + * 3 | f2.cs + * 4 | f3.cs + */ +#keyset[id, num] +compilation_args( + int id : @compilation ref, + int num : int ref, + string arg : string ref +); + +/** + * The expanded arguments that were passed to the extractor for a + * compiler invocation. This is similar to `compilation_args`, but + * for a `@someFile.rsp` argument, it includes the arguments from that + * file, rather than just taking the argument literally. + */ +#keyset[id, num] +compilation_expanded_args( + int id : @compilation ref, + int num : int ref, + string arg : string ref +); + +/** + * The source files that are compiled by a compiler invocation. + * If `id` is for the compiler invocation + * + * csc f1.cs f2.cs f3.cs + * + * then there will be rows for + * + * num | arg + * --- | --- + * 0 | f1.cs + * 1 | f2.cs + * 2 | f3.cs + */ +#keyset[id, num] +compilation_compiling_files( + int id : @compilation ref, + int num : int ref, + int file : @file ref +); + +/** + * The references used by a compiler invocation. + * If `id` is for the compiler invocation + * + * csc f1.cs f2.cs f3.cs /r:ref1.dll /r:ref2.dll /r:ref3.dll + * + * then there will be rows for + * + * num | arg + * --- | --- + * 0 | ref1.dll + * 1 | ref2.dll + * 2 | ref3.dll + */ +#keyset[id, num] +compilation_referencing_files( + int id : @compilation ref, + int num : int ref, + int file : @file ref +); + +/** + * The time taken by the extractor for a compiler invocation. + * + * For each file `num`, there will be rows for + * + * kind | seconds + * ---- | --- + * 1 | CPU seconds used by the extractor frontend + * 2 | Elapsed seconds during the extractor frontend + * 3 | CPU seconds used by the extractor backend + * 4 | Elapsed seconds during the extractor backend + */ +#keyset[id, num, kind] +compilation_time( + int id : @compilation ref, + int num : int ref, + /* kind: + 1 = frontend_cpu_seconds + 2 = frontend_elapsed_seconds + 3 = extractor_cpu_seconds + 4 = extractor_elapsed_seconds + */ + int kind : int ref, + float seconds : float ref +); + +/** + * An error or warning generated by the extractor. + * The diagnostic message `diagnostic` was generated during compiler + * invocation `compilation`, and is the `file_number_diagnostic_number`th + * message generated while extracting the `file_number`th file of that + * invocation. + */ +#keyset[compilation, file_number, file_number_diagnostic_number] +diagnostic_for( + unique int diagnostic : @diagnostic ref, + int compilation : @compilation ref, + int file_number : int ref, + int file_number_diagnostic_number : int ref +); + +diagnostics( + unique int id: @diagnostic, + int severity: int ref, + string error_tag: string ref, + string error_message: string ref, + string full_error_message: string ref, + int location: @location ref +); + +extractor_messages( + unique int id: @extractor_message, + int severity: int ref, + string origin : string ref, + string text : string ref, + string entity : string ref, + int location: @location ref, + string stack_trace : string ref +); + +/** + * If extraction was successful, then `cpu_seconds` and + * `elapsed_seconds` are the CPU time and elapsed time (respectively) + * that extraction took for compiler invocation `id`. + */ +compilation_finished( + unique int id : @compilation ref, + float cpu_seconds : float ref, + float elapsed_seconds : float ref +); + +compilation_assembly( + unique int id : @compilation ref, + int assembly: @assembly ref +) + +// Populated by the CSV extractor +externalData( + int id: @externalDataElement, + string path: string ref, + int column: int ref, + string value: string ref); + +sourceLocationPrefix( + string prefix: string ref); + +/* + * Overlay support + */ + +/** + * The CLI will automatically emit the tuple `databaseMetadata("isOverlay", "true")`, + * along with an `overlayChangedFiles` tuple for each new/modified/deleted file, + * when building an overlay database, and these can be used by the discard predicates. + */ +databaseMetadata( + string metadataKey : string ref, + string value : string ref +); + +overlayChangedFiles( + string path : string ref +); + +/* + * C# dbscheme + */ + +/** ELEMENTS **/ + +@element = @declaration | @stmt | @expr | @modifier | @attribute | @namespace_declaration + | @using_directive | @type_parameter_constraints | @externalDataElement + | @xmllocatable | @asp_element | @namespace | @preprocessor_directive; + +@declaration = @callable | @generic | @assignable | @namespace; + +@named_element = @namespace | @declaration; + +@declaration_with_accessors = @property | @indexer | @event; + +@assignable = @variable | @assignable_with_accessors | @event; + +@assignable_with_accessors = @property | @indexer; + +@attributable = @assembly | @field | @parameter | @operator | @method | @constructor + | @destructor | @callable_accessor | @value_or_ref_type | @declaration_with_accessors + | @local_function | @lambda_expr; + +/** LOCATIONS, ASEMMBLIES, MODULES, FILES and FOLDERS **/ + +@location = @location_default | @assembly; + +@locatable = @declaration_with_accessors | @callable_accessor | @declaration_or_directive + | @diagnostic | @extractor_message | @preprocessor_directive | @attribute | @type_mention | @type_parameter_constraints + | @declaration_with_accessors | @callable_accessor | @operator | @method + | @constructor | @destructor | @field | @local_variable | @parameter | @stmt | @expr + | @xmllocatable | @commentline | @commentblock | @asp_element + +locations_default( + unique int id: @location_default, + int file: @file ref, + int beginLine: int ref, + int beginColumn: int ref, + int endLine: int ref, + int endColumn: int ref); + +locations_mapped( + unique int id: @location_default ref, + int mapped_to: @location_default ref); + +@sourceline = @file | @callable | @xmllocatable; + +numlines( + int element_id: @sourceline ref, + int num_lines: int ref, + int num_code: int ref, + int num_comment: int ref); + +assemblies( + unique int id: @assembly, + int file: @file ref, + string fullname: string ref, + string name: string ref, + string version: string ref); + +files( + unique int id: @file, + string name: string ref); + +folders( + unique int id: @folder, + string name: string ref); + +@container = @folder | @file ; + +containerparent( + int parent: @container ref, + unique int child: @container ref); + +file_extraction_mode( + unique int file: @file ref, + int mode: int ref + /* 0 = normal, 1 = standalone extractor */ + ); + +/** NAMESPACES **/ + +@type_container = @namespace | @type; + +namespaces( + unique int id: @namespace, + string name: string ref); + +namespace_declarations( + unique int id: @namespace_declaration, + int namespace_id: @namespace ref); + +namespace_declaration_location( + unique int id: @namespace_declaration ref, + int loc: @location ref); + +parent_namespace( + unique int child_id: @type_container ref, + int namespace_id: @namespace ref); + +@declaration_or_directive = @namespace_declaration | @type | @using_directive; + +parent_namespace_declaration( + int child_id: @declaration_or_directive ref, // cannot be unique because of partial classes + int namespace_id: @namespace_declaration ref); + +@using_directive = @using_namespace_directive | @using_static_directive; + +using_global( + unique int id: @using_directive ref +); + +using_namespace_directives( + unique int id: @using_namespace_directive, + int namespace_id: @namespace ref); + +using_static_directives( + unique int id: @using_static_directive, + int type_id: @type_or_ref ref); + +using_directive_location( + unique int id: @using_directive ref, + int loc: @location ref); + +@preprocessor_directive = @pragma_warning | @pragma_checksum | @directive_define | @directive_undefine | @directive_warning + | @directive_error | @directive_nullable | @directive_line | @directive_region | @directive_endregion | @directive_if + | @directive_elif | @directive_else | @directive_endif; + +@conditional_directive = @directive_if | @directive_elif; +@branch_directive = @directive_if | @directive_elif | @directive_else; + +directive_ifs( + unique int id: @directive_if, + int branchTaken: int ref, /* 0: false, 1: true */ + int conditionValue: int ref); /* 0: false, 1: true */ + +directive_elifs( + unique int id: @directive_elif, + int branchTaken: int ref, /* 0: false, 1: true */ + int conditionValue: int ref, /* 0: false, 1: true */ + int parent: @directive_if ref, + int index: int ref); + +directive_elses( + unique int id: @directive_else, + int branchTaken: int ref, /* 0: false, 1: true */ + int parent: @directive_if ref, + int index: int ref); + +#keyset[id, start] +directive_endifs( + unique int id: @directive_endif, + unique int start: @directive_if ref); + +directive_define_symbols( + unique int id: @define_symbol_expr ref, + string name: string ref); + +directive_regions( + unique int id: @directive_region, + string name: string ref); + +#keyset[id, start] +directive_endregions( + unique int id: @directive_endregion, + unique int start: @directive_region ref); + +directive_lines( + unique int id: @directive_line, + int kind: int ref); /* 0: default, 1: hidden, 2: numeric, 3: span */ + +directive_line_value( + unique int id: @directive_line ref, + int line: int ref); + +directive_line_file( + unique int id: @directive_line ref, + int file: @file ref); + +directive_line_offset( + unique int id: @directive_line ref, + int offset: int ref); + +directive_line_span( + unique int id: @directive_line ref, + int startLine: int ref, + int startColumn: int ref, + int endLine: int ref, + int endColumn: int ref); + +directive_nullables( + unique int id: @directive_nullable, + int setting: int ref, /* 0: disable, 1: enable, 2: restore */ + int target: int ref); /* 0: none, 1: annotations, 2: warnings */ + +directive_warnings( + unique int id: @directive_warning, + string message: string ref); + +directive_errors( + unique int id: @directive_error, + string message: string ref); + +directive_undefines( + unique int id: @directive_undefine, + string name: string ref); + +directive_defines( + unique int id: @directive_define, + string name: string ref); + +pragma_checksums( + unique int id: @pragma_checksum, + int file: @file ref, + string guid: string ref, + string bytes: string ref); + +pragma_warnings( + unique int id: @pragma_warning, + int kind: int ref /* 0 = disable, 1 = restore */); + +#keyset[id, index] +pragma_warning_error_codes( + int id: @pragma_warning ref, + string errorCode: string ref, + int index: int ref); + +preprocessor_directive_location( + unique int id: @preprocessor_directive ref, + int loc: @location ref); + +preprocessor_directive_compilation( + int id: @preprocessor_directive ref, + int compilation: @compilation ref); + +preprocessor_directive_active( + unique int id: @preprocessor_directive ref, + int active: int ref); /* 0: false, 1: true */ + +/** TYPES **/ + +types( + unique int id: @type, + int kind: int ref, + string name: string ref); + +case @type.kind of + 1 = @bool_type +| 2 = @char_type +| 3 = @decimal_type +| 4 = @sbyte_type +| 5 = @short_type +| 6 = @int_type +| 7 = @long_type +| 8 = @byte_type +| 9 = @ushort_type +| 10 = @uint_type +| 11 = @ulong_type +| 12 = @float_type +| 13 = @double_type +| 14 = @enum_type +| 15 = @struct_type +| 17 = @class_type +| 19 = @interface_type +| 20 = @delegate_type +| 21 = @null_type +| 22 = @type_parameter +| 23 = @pointer_type +| 24 = @nullable_type +| 25 = @array_type +| 26 = @void_type +| 27 = @int_ptr_type +| 28 = @uint_ptr_type +| 29 = @dynamic_type +| 30 = @arglist_type +| 31 = @unknown_type +| 32 = @tuple_type +| 33 = @function_pointer_type +| 34 = @inline_array_type +| 35 = @extension_type + ; + +@simple_type = @bool_type | @char_type | @integral_type | @floating_point_type | @decimal_type; +@integral_type = @signed_integral_type | @unsigned_integral_type; +@signed_integral_type = @sbyte_type | @short_type | @int_type | @long_type; +@unsigned_integral_type = @byte_type | @ushort_type | @uint_type | @ulong_type; +@floating_point_type = @float_type | @double_type; +@value_type = @simple_type | @enum_type | @struct_type | @nullable_type | @int_ptr_type + | @uint_ptr_type | @tuple_type | @void_type | @inline_array_type; +@ref_type = @class_type | @interface_type | @array_type | @delegate_type | @null_type + | @dynamic_type | @extension_type; +@value_or_ref_type = @value_type | @ref_type; + +typerefs( + unique int id: @typeref, + string name: string ref); + +typeref_type( + int id: @typeref ref, + unique int typeId: @type ref); + +@type_or_ref = @type | @typeref; + +array_element_type( + unique int array: @array_type ref, + int dimension: int ref, + int rank: int ref, + int element: @type_or_ref ref); + +nullable_underlying_type( + unique int nullable: @nullable_type ref, + int underlying: @type_or_ref ref); + +pointer_referent_type( + unique int pointer: @pointer_type ref, + int referent: @type_or_ref ref); + +enum_underlying_type( + unique int enum_id: @enum_type ref, + int underlying_type_id: @type_or_ref ref); + +delegate_return_type( + unique int delegate_id: @delegate_type ref, + int return_type_id: @type_or_ref ref); + +function_pointer_return_type( + unique int function_pointer_id: @function_pointer_type ref, + int return_type_id: @type_or_ref ref); + +extension_receiver_type( + unique int extension: @extension_type ref, + int receiver_type_id: @type_or_ref ref); + +extend( + int sub: @type ref, + int super: @type_or_ref ref); + +anonymous_types( + unique int id: @type ref); + +@interface_or_ref = @interface_type | @typeref; + +implement( + int sub: @type ref, + int super: @type_or_ref ref); + +type_location( + int id: @type ref, + int loc: @location ref); + +tuple_underlying_type( + unique int tuple: @tuple_type ref, + int struct: @type_or_ref ref); + +#keyset[tuple, index] +tuple_element( + int tuple: @tuple_type ref, + int index: int ref, + unique int field: @field ref); + +attributes( + unique int id: @attribute, + int kind: int ref, + int type_id: @type_or_ref ref, + int target: @attributable ref); + +case @attribute.kind of + 0 = @attribute_default +| 1 = @attribute_return +| 2 = @attribute_assembly +| 3 = @attribute_module +; + +attribute_location( + int id: @attribute ref, + int loc: @location ref); + +@type_mention_parent = @element | @type_mention; + +type_mention( + unique int id: @type_mention, + int type_id: @type_or_ref ref, + int parent: @type_mention_parent ref); + +type_mention_location( + unique int id: @type_mention ref, + int loc: @location ref); + +@has_type_annotation = @assignable | @type_parameter | @callable | @expr | @delegate_type | @generic | @function_pointer_type; + +/** + * A direct annotation on an entity, for example `string? x;`. + * + * Annotations: + * 2 = reftype is not annotated "!" + * 3 = reftype is annotated "?" + * 4 = readonly ref type / in parameter + * 5 = ref type parameter, return or local variable + * 6 = out parameter + * + * Note that the annotation depends on the element it annotates. + * @assignable: The annotation is on the type of the assignable, for example the variable type. + * @type_parameter: The annotation is on the reftype constraint + * @callable: The annotation is on the return type + * @array_type: The annotation is on the element type + */ +type_annotation(int id: @has_type_annotation ref, int annotation: int ref); + +nullability(unique int nullability: @nullability, int kind: int ref); + +case @nullability.kind of + 0 = @oblivious +| 1 = @not_annotated +| 2 = @annotated +; + +#keyset[parent, index] +nullability_parent(int nullability: @nullability ref, int index: int ref, int parent: @nullability ref) + +type_nullability(int id: @has_type_annotation ref, int nullability: @nullability ref); + +/** + * The nullable flow state of an expression, as determined by Roslyn. + * 0 = none (default, not populated) + * 1 = not null + * 2 = maybe null + */ +expr_flowstate(unique int id: @expr ref, int state: int ref); + +/** GENERICS **/ + +@generic = @type | @method | @local_function; + +type_parameters( + unique int id: @type_parameter ref, + int index: int ref, + int generic_id: @generic ref, + int variance: int ref /* none = 0, out = 1, in = 2 */); + +#keyset[constructed_id, index] +type_arguments( + int id: @type_or_ref ref, + int index: int ref, + int constructed_id: @generic_or_ref ref); + +@generic_or_ref = @generic | @typeref; + +constructed_generic( + unique int constructed: @generic ref, + int generic: @generic_or_ref ref); + +type_parameter_constraints( + unique int id: @type_parameter_constraints, + int param_id: @type_parameter ref); + +type_parameter_constraints_location( + int id: @type_parameter_constraints ref, + int loc: @location ref); + +general_type_parameter_constraints( + int id: @type_parameter_constraints ref, + int kind: int ref /* class = 1, struct = 2, new = 3 */); + +specific_type_parameter_constraints( + int id: @type_parameter_constraints ref, + int base_id: @type_or_ref ref); + +specific_type_parameter_nullability( + int id: @type_parameter_constraints ref, + int base_id: @type_or_ref ref, + int nullability: @nullability ref); + +/** FUNCTION POINTERS */ + +function_pointer_calling_conventions( + int id: @function_pointer_type ref, + int kind: int ref); + +#keyset[id, index] +has_unmanaged_calling_conventions( + int id: @function_pointer_type ref, + int index: int ref, + int conv_id: @type_or_ref ref); + +/** MODIFIERS */ + +@modifiable = @modifiable_direct | @event_accessor; + +@modifiable_direct = @member | @accessor | @local_function | @anonymous_function_expr; + +modifiers( + unique int id: @modifier, + string name: string ref); + +has_modifiers( + int id: @modifiable_direct ref, + int mod_id: @modifier ref); + +/** MEMBERS **/ + +@member = @method | @constructor | @destructor | @field | @property | @event | @operator | @indexer | @type; + +@named_exprorstmt = @goto_stmt | @labeled_stmt | @expr; + +@virtualizable = @method | @property | @indexer | @event | @operator; + +exprorstmt_name( + unique int parent_id: @named_exprorstmt ref, + string name: string ref); + +nested_types( + unique int id: @type ref, + int declaring_type_id: @type ref, + int unbound_id: @type ref); + +properties( + unique int id: @property, + string name: string ref, + int declaring_type_id: @type ref, + int type_id: @type_or_ref ref, + int unbound_id: @property ref); + +property_location( + int id: @property ref, + int loc: @location ref); + +indexers( + unique int id: @indexer, + string name: string ref, + int declaring_type_id: @type ref, + int type_id: @type_or_ref ref, + int unbound_id: @indexer ref); + +indexer_location( + int id: @indexer ref, + int loc: @location ref); + +accessors( + unique int id: @accessor, + int kind: int ref, + string name: string ref, + int declaring_member_id: @member ref, + int unbound_id: @accessor ref); + +case @accessor.kind of + 1 = @getter +| 2 = @setter + ; + +init_only_accessors( + unique int id: @accessor ref); + +accessor_location( + int id: @accessor ref, + int loc: @location ref); + +events( + unique int id: @event, + string name: string ref, + int declaring_type_id: @type ref, + int type_id: @type_or_ref ref, + int unbound_id: @event ref); + +event_location( + int id: @event ref, + int loc: @location ref); + +event_accessors( + unique int id: @event_accessor, + int kind: int ref, + string name: string ref, + int declaring_event_id: @event ref, + int unbound_id: @event_accessor ref); + +case @event_accessor.kind of + 1 = @add_event_accessor +| 2 = @remove_event_accessor + ; + +event_accessor_location( + int id: @event_accessor ref, + int loc: @location ref); + +operators( + unique int id: @operator, + string name: string ref, + string symbol: string ref, + int declaring_type_id: @type ref, + int type_id: @type_or_ref ref, + int unbound_id: @operator ref); + +operator_location( + int id: @operator ref, + int loc: @location ref); + +constant_value( + int id: @variable ref, + string value: string ref); + +/** CALLABLES **/ + +@callable = @method | @constructor | @destructor | @operator | @callable_accessor | @anonymous_function_expr | @local_function; + +@callable_accessor = @accessor | @event_accessor; + +methods( + unique int id: @method, + string name: string ref, + int declaring_type_id: @type ref, + int type_id: @type_or_ref ref, + int unbound_id: @method ref); + +method_location( + int id: @method ref, + int loc: @location ref); + +constructors( + unique int id: @constructor, + string name: string ref, + int declaring_type_id: @type ref, + int unbound_id: @constructor ref); + +constructor_location( + int id: @constructor ref, + int loc: @location ref); + +destructors( + unique int id: @destructor, + string name: string ref, + int declaring_type_id: @type ref, + int unbound_id: @destructor ref); + +destructor_location( + int id: @destructor ref, + int loc: @location ref); + +overrides( + int id: @callable ref, + int base_id: @callable ref); + +explicitly_implements( + int id: @member ref, + int interface_id: @interface_or_ref ref); + +local_functions( + unique int id: @local_function, + string name: string ref, + int return_type: @type ref, + int unbound_id: @local_function ref); + +local_function_stmts( + unique int fn: @local_function_stmt ref, + int stmt: @local_function ref); + +/** VARIABLES **/ + +@variable = @local_scope_variable | @field; + +@local_scope_variable = @local_variable | @parameter; + +fields( + unique int id: @field, + int kind: int ref, + string name: string ref, + int declaring_type_id: @type ref, + int type_id: @type_or_ref ref, + int unbound_id: @field ref); + +case @field.kind of + 1 = @addressable_field +| 2 = @constant + ; + +field_location( + int id: @field ref, + int loc: @location ref); + +localvars( + unique int id: @local_variable, + int kind: int ref, + string name: string ref, + int implicitly_typed: int ref /* 0 = no, 1 = yes */, + int type_id: @type_or_ref ref, + int parent_id: @local_var_decl_expr ref); + +case @local_variable.kind of + 1 = @addressable_local_variable +| 2 = @local_constant +| 3 = @local_variable_ref + ; + +localvar_location( + unique int id: @local_variable ref, + int loc: @location ref); + +@parameterizable = @callable | @delegate_type | @indexer | @function_pointer_type | @extension_type; + +#keyset[name, parent_id] +#keyset[index, parent_id] +params( + unique int id: @parameter, + string name: string ref, + int type_id: @type_or_ref ref, + int index: int ref, + int mode: int ref, /* value = 0, ref = 1, out = 2, params/array = 3, this = 4, in = 5, ref readonly = 6 */ + int parent_id: @parameterizable ref, + int unbound_id: @parameter ref); + +param_location( + int id: @parameter ref, + int loc: @location ref); + +@has_scoped_annotation = @local_scope_variable + +scoped_annotation( + int id: @has_scoped_annotation ref, + int kind: int ref // scoped ref = 1, scoped value = 2 + ); + +/** STATEMENTS **/ + +@exprorstmt_parent = @control_flow_element | @top_level_exprorstmt_parent; + +statements( + unique int id: @stmt, + int kind: int ref); + +#keyset[index, parent] +stmt_parent( + unique int stmt: @stmt ref, + int index: int ref, + int parent: @control_flow_element ref); + +@top_level_stmt_parent = @callable; + +// [index, parent] is not a keyset because the same parent may be compiled multiple times +stmt_parent_top_level( + unique int stmt: @stmt ref, + int index: int ref, + int parent: @top_level_stmt_parent ref); + +case @stmt.kind of + 1 = @block_stmt +| 2 = @expr_stmt +| 3 = @if_stmt +| 4 = @switch_stmt +| 5 = @while_stmt +| 6 = @do_stmt +| 7 = @for_stmt +| 8 = @foreach_stmt +| 9 = @break_stmt +| 10 = @continue_stmt +| 11 = @goto_stmt +| 12 = @goto_case_stmt +| 13 = @goto_default_stmt +| 14 = @throw_stmt +| 15 = @return_stmt +| 16 = @yield_stmt +| 17 = @try_stmt +| 18 = @checked_stmt +| 19 = @unchecked_stmt +| 20 = @lock_stmt +| 21 = @using_block_stmt +| 22 = @var_decl_stmt +| 23 = @const_decl_stmt +| 24 = @empty_stmt +| 25 = @unsafe_stmt +| 26 = @fixed_stmt +| 27 = @label_stmt +| 28 = @catch +| 29 = @case_stmt +| 30 = @local_function_stmt +| 31 = @using_decl_stmt + ; + +@using_stmt = @using_block_stmt | @using_decl_stmt; + +@labeled_stmt = @label_stmt | @case; + +@decl_stmt = @var_decl_stmt | @const_decl_stmt | @using_decl_stmt; + +@cond_stmt = @if_stmt | @switch_stmt; + +@loop_stmt = @while_stmt | @do_stmt | @for_stmt | @foreach_stmt; + +@jump_stmt = @break_stmt | @goto_any_stmt | @continue_stmt | @throw_stmt | @return_stmt + | @yield_stmt; + +@goto_any_stmt = @goto_default_stmt | @goto_case_stmt | @goto_stmt; + + +stmt_location( + unique int id: @stmt ref, + int loc: @location ref); + +catch_type( + unique int catch_id: @catch ref, + int type_id: @type_or_ref ref, + int kind: int ref /* explicit = 1, implicit = 2 */); + +foreach_stmt_info( + unique int id: @foreach_stmt ref, + int kind: int ref /* non-async = 1, async = 2 */); + +@foreach_symbol = @method | @property | @type_or_ref; + +#keyset[id, kind] +foreach_stmt_desugar( + int id: @foreach_stmt ref, + int symbol: @foreach_symbol ref, + int kind: int ref /* GetEnumeratorMethod = 1, CurrentProperty = 2, MoveNextMethod = 3, DisposeMethod = 4, ElementType = 5 */); + +/** EXPRESSIONS **/ + +expressions( + unique int id: @expr, + int kind: int ref, + int type_id: @type_or_ref ref); + +#keyset[index, parent] +expr_parent( + unique int expr: @expr ref, + int index: int ref, + int parent: @control_flow_element ref); + +@top_level_expr_parent = @attribute | @field | @property | @indexer | @parameter | @directive_if | @directive_elif; + +@top_level_exprorstmt_parent = @top_level_expr_parent | @top_level_stmt_parent; + +// [index, parent] is not a keyset because the same parent may be compiled multiple times +expr_parent_top_level( + unique int expr: @expr ref, + int index: int ref, + int parent: @top_level_exprorstmt_parent ref); + +case @expr.kind of +/* literal */ + 1 = @bool_literal_expr +| 2 = @char_literal_expr +| 3 = @decimal_literal_expr +| 4 = @int_literal_expr +| 5 = @long_literal_expr +| 6 = @uint_literal_expr +| 7 = @ulong_literal_expr +| 8 = @float_literal_expr +| 9 = @double_literal_expr +| 10 = @utf16_string_literal_expr +| 11 = @null_literal_expr +/* primary & unary */ +| 12 = @this_access_expr +| 13 = @base_access_expr +| 14 = @local_variable_access_expr +| 15 = @parameter_access_expr +| 16 = @field_access_expr +| 17 = @property_access_expr +| 18 = @method_access_expr +| 19 = @event_access_expr +| 20 = @indexer_access_expr +| 21 = @array_access_expr +| 22 = @type_access_expr +| 23 = @typeof_expr +| 24 = @method_invocation_expr +| 25 = @delegate_invocation_expr +| 26 = @operator_invocation_expr +| 27 = @cast_expr +| 28 = @object_creation_expr +| 29 = @explicit_delegate_creation_expr +| 30 = @implicit_delegate_creation_expr +| 31 = @array_creation_expr +| 32 = @default_expr +| 33 = @plus_expr +| 34 = @minus_expr +| 35 = @bit_not_expr +| 36 = @log_not_expr +| 37 = @post_incr_expr +| 38 = @post_decr_expr +| 39 = @pre_incr_expr +| 40 = @pre_decr_expr +/* multiplicative */ +| 41 = @mul_expr +| 42 = @div_expr +| 43 = @rem_expr +/* additive */ +| 44 = @add_expr +| 45 = @sub_expr +/* shift */ +| 46 = @lshift_expr +| 47 = @rshift_expr +/* relational */ +| 48 = @lt_expr +| 49 = @gt_expr +| 50 = @le_expr +| 51 = @ge_expr +/* equality */ +| 52 = @eq_expr +| 53 = @ne_expr +/* logical */ +| 54 = @bit_and_expr +| 55 = @bit_xor_expr +| 56 = @bit_or_expr +| 57 = @log_and_expr +| 58 = @log_or_expr +/* type testing */ +| 59 = @is_expr +| 60 = @as_expr +/* null coalescing */ +| 61 = @null_coalescing_expr +/* conditional */ +| 62 = @conditional_expr +/* assignment */ +| 63 = @simple_assign_expr +| 64 = @assign_add_expr +| 65 = @assign_sub_expr +| 66 = @assign_mul_expr +| 67 = @assign_div_expr +| 68 = @assign_rem_expr +| 69 = @assign_and_expr +| 70 = @assign_xor_expr +| 71 = @assign_or_expr +| 72 = @assign_lshift_expr +| 73 = @assign_rshift_expr +/* more */ +| 74 = @object_init_expr +| 75 = @collection_init_expr +| 76 = @array_init_expr +| 77 = @checked_expr +| 78 = @unchecked_expr +| 79 = @constructor_init_expr +| 80 = @add_event_expr +| 81 = @remove_event_expr +| 82 = @par_expr +| 83 = @local_var_decl_expr +| 84 = @lambda_expr +| 85 = @anonymous_method_expr +| 86 = @namespace_expr +/* dynamic */ +| 92 = @dynamic_element_access_expr +| 93 = @dynamic_member_access_expr +/* unsafe */ +| 100 = @pointer_indirection_expr +| 101 = @address_of_expr +| 102 = @sizeof_expr +/* async */ +| 103 = @await_expr +/* C# 6.0 */ +| 104 = @nameof_expr +| 105 = @interpolated_string_expr +| 106 = @unknown_expr +/* C# 7.0 */ +| 107 = @throw_expr +| 108 = @tuple_expr +| 109 = @local_function_invocation_expr +| 110 = @ref_expr +| 111 = @discard_expr +/* C# 8.0 */ +| 112 = @range_expr +| 113 = @index_expr +| 114 = @switch_expr +| 115 = @recursive_pattern_expr +| 116 = @property_pattern_expr +| 117 = @positional_pattern_expr +| 118 = @switch_case_expr +| 119 = @assign_coalesce_expr +| 120 = @suppress_nullable_warning_expr +| 121 = @namespace_access_expr +/* C# 9.0 */ +| 122 = @lt_pattern_expr +| 123 = @gt_pattern_expr +| 124 = @le_pattern_expr +| 125 = @ge_pattern_expr +| 126 = @not_pattern_expr +| 127 = @and_pattern_expr +| 128 = @or_pattern_expr +| 129 = @function_pointer_invocation_expr +| 130 = @with_expr +/* C# 11.0 */ +| 131 = @list_pattern_expr +| 132 = @slice_pattern_expr +| 133 = @urshift_expr +| 134 = @assign_urshift_expr +| 135 = @utf8_string_literal_expr +/* C# 12.0 */ +| 136 = @collection_expr +| 137 = @spread_element_expr +| 138 = @interpolated_string_insert_expr +/* Preprocessor */ +| 999 = @define_symbol_expr +; + +@switch = @switch_stmt | @switch_expr; +@case = @case_stmt | @switch_case_expr; +@pattern_match = @case | @is_expr; +@unary_pattern_expr = @not_pattern_expr; +@relational_pattern_expr = @gt_pattern_expr | @lt_pattern_expr | @ge_pattern_expr | @le_pattern_expr; +@binary_pattern_expr = @and_pattern_expr | @or_pattern_expr; + +@integer_literal_expr = @int_literal_expr | @long_literal_expr | @uint_literal_expr | @ulong_literal_expr; +@real_literal_expr = @float_literal_expr | @double_literal_expr | @decimal_literal_expr; +@string_literal_expr = @utf16_string_literal_expr | @utf8_string_literal_expr; +@literal_expr = @bool_literal_expr | @char_literal_expr | @integer_literal_expr | @real_literal_expr + | @string_literal_expr | @null_literal_expr; + +@assign_expr = @simple_assign_expr | @assign_op_expr | @local_var_decl_expr; +@assign_op_call_expr = @assign_arith_expr | @assign_bitwise_expr +@assign_op_expr = @assign_op_call_expr | @assign_event_expr | @assign_coalesce_expr; +@assign_event_expr = @add_event_expr | @remove_event_expr; + +@add_operation = @add_expr | @assign_add_expr; +@sub_operation = @sub_expr | @assign_sub_expr; +@mul_operation = @mul_expr | @assign_mul_expr; +@div_operation = @div_expr | @assign_div_expr; +@rem_operation = @rem_expr | @assign_rem_expr; +@and_operation = @bit_and_expr | @assign_and_expr; +@xor_operation = @bit_xor_expr | @assign_xor_expr; +@or_operation = @bit_or_expr | @assign_or_expr; +@lshift_operation = @lshift_expr | @assign_lshift_expr; +@rshift_operation = @rshift_expr | @assign_rshift_expr; +@urshift_operation = @urshift_expr | @assign_urshift_expr; +@null_coalescing_operation = @null_coalescing_expr | @assign_coalesce_expr; + +@assign_arith_expr = @assign_add_expr | @assign_sub_expr | @assign_mul_expr | @assign_div_expr + | @assign_rem_expr +@assign_bitwise_expr = @assign_and_expr | @assign_or_expr | @assign_xor_expr + | @assign_lshift_expr | @assign_rshift_expr | @assign_urshift_expr; + +@member_access_expr = @field_access_expr | @property_access_expr | @indexer_access_expr | @event_access_expr + | @method_access_expr | @type_access_expr | @dynamic_member_access_expr; +@access_expr = @member_access_expr | @this_access_expr | @base_access_expr | @assignable_access_expr | @namespace_access_expr; +@element_access_expr = @indexer_access_expr | @array_access_expr | @dynamic_element_access_expr; + +@local_variable_access = @local_variable_access_expr | @local_var_decl_expr; +@local_scope_variable_access_expr = @parameter_access_expr | @local_variable_access; +@variable_access_expr = @local_scope_variable_access_expr | @field_access_expr; + +@assignable_access_expr = @variable_access_expr | @property_access_expr | @element_access_expr + | @event_access_expr | @dynamic_member_access_expr; + +@objectorcollection_init_expr = @object_init_expr | @collection_init_expr; + +@delegate_creation_expr = @explicit_delegate_creation_expr | @implicit_delegate_creation_expr; + +@bin_arith_expr = @mul_expr | @div_expr | @rem_expr | @add_expr | @sub_expr; +@bin_arith_operation = @mul_operation | @div_operation | @rem_operation | @add_operation | @sub_operation; + +@incr_operation = @pre_incr_expr | @post_incr_expr; +@decr_operation = @pre_decr_expr | @post_decr_expr; +@mut_operation = @incr_operation | @decr_operation; +@un_arith_operation = @plus_expr | @minus_expr | @mut_operation; +@arith_operation = @bin_arith_operation | @un_arith_operation; + +@ternary_log_operation = @conditional_expr; +@bin_log_operation = @log_and_expr | @log_or_expr | @null_coalescing_operation; +@un_log_operation = @log_not_expr; +@log_operation = @un_log_operation | @bin_log_operation | @ternary_log_operation; + +@bin_bit_expr = @bit_and_expr | @bit_or_expr | @bit_xor_expr | @lshift_expr + | @rshift_expr | @urshift_expr; +@bin_bit_operation = @and_operation | @or_operation | @xor_operation | @lshift_operation + | @rshift_operation | @urshift_operation; +@un_bit_expr = @bit_not_expr; +@un_bit_operation = @un_bit_expr; +@bit_expr = @un_bit_expr | @bin_bit_expr; +@bit_operation = @un_bit_operation | @bin_bit_operation; + +@equality_op_expr = @eq_expr | @ne_expr; +@rel_op_expr = @gt_expr | @lt_expr| @ge_expr | @le_expr; +@comp_expr = @equality_op_expr | @rel_op_expr; + +@operation_expr = @un_operation | @bin_operation | @ternary_operation; + +@ternary_operation = @ternary_log_operation; +@bin_operation = @assign_expr | @bin_arith_operation | @bin_log_operation | @bin_bit_operation | @comp_expr; +@un_operation = @un_arith_operation | @un_log_operation | @un_bit_operation | @sizeof_expr + | @pointer_indirection_expr | @address_of_expr; + +@anonymous_function_expr = @lambda_expr | @anonymous_method_expr; + +@op_invoke_expr = @operator_invocation_expr | @assign_op_call_expr +@call = @method_invocation_expr | @constructor_init_expr | @op_invoke_expr + | @delegate_invocation_expr | @object_creation_expr | @call_access_expr + | @local_function_invocation_expr | @function_pointer_invocation_expr; + +@call_access_expr = @property_access_expr | @event_access_expr | @indexer_access_expr; + +@late_bindable_expr = @dynamic_element_access_expr | @dynamic_member_access_expr + | @object_creation_expr | @method_invocation_expr | @op_invoke_expr; + +@throw_element = @throw_expr | @throw_stmt; + +@implicitly_typeable_object_creation_expr = @object_creation_expr | @explicit_delegate_creation_expr; + +implicitly_typed_array_creation( + unique int id: @array_creation_expr ref); + +explicitly_sized_array_creation( + unique int id: @array_creation_expr ref); + +stackalloc_array_creation( + unique int id: @array_creation_expr ref); + +implicitly_typed_object_creation( + unique int id: @implicitly_typeable_object_creation_expr ref); + +mutator_invocation_mode( + unique int id: @operator_invocation_expr ref, + int mode: int ref /* prefix = 1, postfix = 2*/); + +expr_value( + unique int id: @expr ref, + string value: string ref); + +expr_call( + unique int caller_id: @expr ref, + int target_id: @callable ref); + +expr_access( + unique int accesser_id: @access_expr ref, + int target_id: @accessible ref); + +@accessible = @method | @assignable | @local_function | @namespace; + +expr_location( + unique int id: @expr ref, + int loc: @location ref); + +dynamic_member_name( + unique int id: @late_bindable_expr ref, + string name: string ref); + +@qualifiable_expr = @member_access_expr + | @method_invocation_expr + | @element_access_expr + | @assign_op_call_expr; + +conditional_access( + unique int id: @qualifiable_expr ref); + +expr_argument( + unique int id: @expr ref, + int mode: int ref); + /* mode is the same as params: value = 0, ref = 1, out = 2 */ + +expr_argument_name( + unique int id: @expr ref, + string name: string ref); + +lambda_expr_return_type( + unique int id: @lambda_expr ref, + int type_id: @type_or_ref ref); + +/* Compiler generated */ + +compiler_generated(unique int id: @element ref); + +/** CONTROL/DATA FLOW **/ + +@control_flow_element = @stmt | @expr | @parameter; + +/* XML Files */ + +xmlEncoding ( + unique int id: @file ref, + string encoding: string ref); + +xmlDTDs( + unique int id: @xmldtd, + string root: string ref, + string publicId: string ref, + string systemId: string ref, + int fileid: @file ref); + +xmlElements( + unique int id: @xmlelement, + string name: string ref, + int parentid: @xmlparent ref, + int idx: int ref, + int fileid: @file ref); + +xmlAttrs( + unique int id: @xmlattribute, + int elementid: @xmlelement ref, + string name: string ref, + string value: string ref, + int idx: int ref, + int fileid: @file ref); + +xmlNs( + int id: @xmlnamespace, + string prefixName: string ref, + string URI: string ref, + int fileid: @file ref); + +xmlHasNs( + int elementId: @xmlnamespaceable ref, + int nsId: @xmlnamespace ref, + int fileid: @file ref); + +xmlComments( + unique int id: @xmlcomment, + string text: string ref, + int parentid: @xmlparent ref, + int fileid: @file ref); + +xmlChars( + unique int id: @xmlcharacters, + string text: string ref, + int parentid: @xmlparent ref, + int idx: int ref, + int isCDATA: int ref, + int fileid: @file ref); + +@xmlparent = @file | @xmlelement; +@xmlnamespaceable = @xmlelement | @xmlattribute; + +xmllocations( + int xmlElement: @xmllocatable ref, + int location: @location_default ref); + +@xmllocatable = @xmlcharacters | @xmlelement | @xmlcomment | @xmlattribute | @xmldtd | @file | @xmlnamespace; + +/* Comments */ + +commentline( + unique int id: @commentline, + int kind: int ref, + string text: string ref, + string rawtext: string ref); + +case @commentline.kind of + 0 = @singlelinecomment +| 1 = @xmldoccomment +| 2 = @multilinecomment; + +commentline_location( + unique int id: @commentline ref, + int loc: @location ref); + +commentblock( + unique int id : @commentblock); + +commentblock_location( + unique int id: @commentblock ref, + int loc: @location ref); + +commentblock_binding( + int id: @commentblock ref, + int entity: @element ref, + int bindtype: int ref); /* 0: Parent, 1: Best, 2: Before, 3: After */ + +commentblock_child( + int id: @commentblock ref, + int commentline: @commentline ref, + int index: int ref); + +/* ASP.NET */ + +case @asp_element.kind of + 0=@asp_close_tag +| 1=@asp_code +| 2=@asp_comment +| 3=@asp_data_binding +| 4=@asp_directive +| 5=@asp_open_tag +| 6=@asp_quoted_string +| 7=@asp_text +| 8=@asp_xml_directive; + +@asp_attribute = @asp_code | @asp_data_binding | @asp_quoted_string; + +asp_elements( + unique int id: @asp_element, + int kind: int ref, + int loc: @location ref); + +asp_comment_server(unique int comment: @asp_comment ref); +asp_code_inline(unique int code: @asp_code ref); +asp_directive_attribute( + int directive: @asp_directive ref, + int index: int ref, + string name: string ref, + int value: @asp_quoted_string ref); +asp_directive_name( + unique int directive: @asp_directive ref, + string name: string ref); +asp_element_body( + unique int element: @asp_element ref, + string body: string ref); +asp_tag_attribute( + int tag: @asp_open_tag ref, + int index: int ref, + string name: string ref, + int attribute: @asp_attribute ref); +asp_tag_name( + unique int tag: @asp_open_tag ref, + string name: string ref); +asp_tag_isempty(int tag: @asp_open_tag ref); diff --git a/csharp/downgrades/d13c4c187d7318fd2b8f35c7e8d7f4dc26be68b1/semmlecode.csharp.dbscheme b/csharp/downgrades/d13c4c187d7318fd2b8f35c7e8d7f4dc26be68b1/semmlecode.csharp.dbscheme new file mode 100644 index 00000000000..3cabc77473c --- /dev/null +++ b/csharp/downgrades/d13c4c187d7318fd2b8f35c7e8d7f4dc26be68b1/semmlecode.csharp.dbscheme @@ -0,0 +1,1505 @@ +/* This is a dummy line to alter the dbscheme, so we can make a database upgrade + * without actually changing any of the dbscheme predicates. It contains a date + * to allow for such updates in the future as well. + * + * 2021-07-14 + * + * DO NOT remove this comment carelessly, since it can revert the dbscheme back to a + * previously seen state (matching a previously seen SHA), which would make the upgrade + * mechanism not work properly. + */ + +/** + * An invocation of the compiler. Note that more than one file may be + * compiled per invocation. For example, this command compiles three + * source files: + * + * csc f1.cs f2.cs f3.cs + * + * The `id` simply identifies the invocation, while `cwd` is the working + * directory from which the compiler was invoked. + */ +compilations( + unique int id : @compilation, + string cwd : string ref +); + +compilation_info( + int id : @compilation ref, + string info_key: string ref, + string info_value: string ref +) + +/** + * The arguments that were passed to the extractor for a compiler + * invocation. If `id` is for the compiler invocation + * + * csc f1.cs f2.cs f3.cs + * + * then typically there will be rows for + * + * num | arg + * --- | --- + * 0 | --compiler + * 1 | *path to compiler* + * 2 | f1.cs + * 3 | f2.cs + * 4 | f3.cs + */ +#keyset[id, num] +compilation_args( + int id : @compilation ref, + int num : int ref, + string arg : string ref +); + +/** + * The expanded arguments that were passed to the extractor for a + * compiler invocation. This is similar to `compilation_args`, but + * for a `@someFile.rsp` argument, it includes the arguments from that + * file, rather than just taking the argument literally. + */ +#keyset[id, num] +compilation_expanded_args( + int id : @compilation ref, + int num : int ref, + string arg : string ref +); + +/** + * The source files that are compiled by a compiler invocation. + * If `id` is for the compiler invocation + * + * csc f1.cs f2.cs f3.cs + * + * then there will be rows for + * + * num | arg + * --- | --- + * 0 | f1.cs + * 1 | f2.cs + * 2 | f3.cs + */ +#keyset[id, num] +compilation_compiling_files( + int id : @compilation ref, + int num : int ref, + int file : @file ref +); + +/** + * The references used by a compiler invocation. + * If `id` is for the compiler invocation + * + * csc f1.cs f2.cs f3.cs /r:ref1.dll /r:ref2.dll /r:ref3.dll + * + * then there will be rows for + * + * num | arg + * --- | --- + * 0 | ref1.dll + * 1 | ref2.dll + * 2 | ref3.dll + */ +#keyset[id, num] +compilation_referencing_files( + int id : @compilation ref, + int num : int ref, + int file : @file ref +); + +/** + * The time taken by the extractor for a compiler invocation. + * + * For each file `num`, there will be rows for + * + * kind | seconds + * ---- | --- + * 1 | CPU seconds used by the extractor frontend + * 2 | Elapsed seconds during the extractor frontend + * 3 | CPU seconds used by the extractor backend + * 4 | Elapsed seconds during the extractor backend + */ +#keyset[id, num, kind] +compilation_time( + int id : @compilation ref, + int num : int ref, + /* kind: + 1 = frontend_cpu_seconds + 2 = frontend_elapsed_seconds + 3 = extractor_cpu_seconds + 4 = extractor_elapsed_seconds + */ + int kind : int ref, + float seconds : float ref +); + +/** + * An error or warning generated by the extractor. + * The diagnostic message `diagnostic` was generated during compiler + * invocation `compilation`, and is the `file_number_diagnostic_number`th + * message generated while extracting the `file_number`th file of that + * invocation. + */ +#keyset[compilation, file_number, file_number_diagnostic_number] +diagnostic_for( + unique int diagnostic : @diagnostic ref, + int compilation : @compilation ref, + int file_number : int ref, + int file_number_diagnostic_number : int ref +); + +diagnostics( + unique int id: @diagnostic, + int severity: int ref, + string error_tag: string ref, + string error_message: string ref, + string full_error_message: string ref, + int location: @location ref +); + +extractor_messages( + unique int id: @extractor_message, + int severity: int ref, + string origin : string ref, + string text : string ref, + string entity : string ref, + int location: @location ref, + string stack_trace : string ref +); + +/** + * If extraction was successful, then `cpu_seconds` and + * `elapsed_seconds` are the CPU time and elapsed time (respectively) + * that extraction took for compiler invocation `id`. + */ +compilation_finished( + unique int id : @compilation ref, + float cpu_seconds : float ref, + float elapsed_seconds : float ref +); + +compilation_assembly( + unique int id : @compilation ref, + int assembly: @assembly ref +) + +// Populated by the CSV extractor +externalData( + int id: @externalDataElement, + string path: string ref, + int column: int ref, + string value: string ref); + +sourceLocationPrefix( + string prefix: string ref); + +/* + * Overlay support + */ + +/** + * The CLI will automatically emit the tuple `databaseMetadata("isOverlay", "true")`, + * along with an `overlayChangedFiles` tuple for each new/modified/deleted file, + * when building an overlay database, and these can be used by the discard predicates. + */ +databaseMetadata( + string metadataKey : string ref, + string value : string ref +); + +overlayChangedFiles( + string path : string ref +); + +/* + * C# dbscheme + */ + +/** ELEMENTS **/ + +@element = @declaration | @stmt | @expr | @modifier | @attribute | @namespace_declaration + | @using_directive | @type_parameter_constraints | @externalDataElement + | @xmllocatable | @asp_element | @namespace | @preprocessor_directive; + +@declaration = @callable | @generic | @assignable | @namespace; + +@named_element = @namespace | @declaration; + +@declaration_with_accessors = @property | @indexer | @event; + +@assignable = @variable | @assignable_with_accessors | @event; + +@assignable_with_accessors = @property | @indexer; + +@attributable = @assembly | @field | @parameter | @operator | @method | @constructor + | @destructor | @callable_accessor | @value_or_ref_type | @declaration_with_accessors + | @local_function | @lambda_expr; + +/** LOCATIONS, ASEMMBLIES, MODULES, FILES and FOLDERS **/ + +@location = @location_default | @assembly; + +@locatable = @declaration_with_accessors | @callable_accessor | @declaration_or_directive + | @diagnostic | @extractor_message | @preprocessor_directive | @attribute | @type_mention | @type_parameter_constraints + | @declaration_with_accessors | @callable_accessor | @operator | @method + | @constructor | @destructor | @field | @local_variable | @parameter | @stmt | @expr + | @xmllocatable | @commentline | @commentblock | @asp_element + +locations_default( + unique int id: @location_default, + int file: @file ref, + int beginLine: int ref, + int beginColumn: int ref, + int endLine: int ref, + int endColumn: int ref); + +locations_mapped( + unique int id: @location_default ref, + int mapped_to: @location_default ref); + +@sourceline = @file | @callable | @xmllocatable; + +numlines( + int element_id: @sourceline ref, + int num_lines: int ref, + int num_code: int ref, + int num_comment: int ref); + +assemblies( + unique int id: @assembly, + int file: @file ref, + string fullname: string ref, + string name: string ref, + string version: string ref); + +files( + unique int id: @file, + string name: string ref); + +folders( + unique int id: @folder, + string name: string ref); + +@container = @folder | @file ; + +containerparent( + int parent: @container ref, + unique int child: @container ref); + +file_extraction_mode( + unique int file: @file ref, + int mode: int ref + /* 0 = normal, 1 = standalone extractor */ + ); + +/** NAMESPACES **/ + +@type_container = @namespace | @type; + +namespaces( + unique int id: @namespace, + string name: string ref); + +namespace_declarations( + unique int id: @namespace_declaration, + int namespace_id: @namespace ref); + +namespace_declaration_location( + unique int id: @namespace_declaration ref, + int loc: @location ref); + +parent_namespace( + unique int child_id: @type_container ref, + int namespace_id: @namespace ref); + +@declaration_or_directive = @namespace_declaration | @type | @using_directive; + +parent_namespace_declaration( + int child_id: @declaration_or_directive ref, // cannot be unique because of partial classes + int namespace_id: @namespace_declaration ref); + +@using_directive = @using_namespace_directive | @using_static_directive; + +using_global( + unique int id: @using_directive ref +); + +using_namespace_directives( + unique int id: @using_namespace_directive, + int namespace_id: @namespace ref); + +using_static_directives( + unique int id: @using_static_directive, + int type_id: @type_or_ref ref); + +using_directive_location( + unique int id: @using_directive ref, + int loc: @location ref); + +@preprocessor_directive = @pragma_warning | @pragma_checksum | @directive_define | @directive_undefine | @directive_warning + | @directive_error | @directive_nullable | @directive_line | @directive_region | @directive_endregion | @directive_if + | @directive_elif | @directive_else | @directive_endif; + +@conditional_directive = @directive_if | @directive_elif; +@branch_directive = @directive_if | @directive_elif | @directive_else; + +directive_ifs( + unique int id: @directive_if, + int branchTaken: int ref, /* 0: false, 1: true */ + int conditionValue: int ref); /* 0: false, 1: true */ + +directive_elifs( + unique int id: @directive_elif, + int branchTaken: int ref, /* 0: false, 1: true */ + int conditionValue: int ref, /* 0: false, 1: true */ + int parent: @directive_if ref, + int index: int ref); + +directive_elses( + unique int id: @directive_else, + int branchTaken: int ref, /* 0: false, 1: true */ + int parent: @directive_if ref, + int index: int ref); + +#keyset[id, start] +directive_endifs( + unique int id: @directive_endif, + unique int start: @directive_if ref); + +directive_define_symbols( + unique int id: @define_symbol_expr ref, + string name: string ref); + +directive_regions( + unique int id: @directive_region, + string name: string ref); + +#keyset[id, start] +directive_endregions( + unique int id: @directive_endregion, + unique int start: @directive_region ref); + +directive_lines( + unique int id: @directive_line, + int kind: int ref); /* 0: default, 1: hidden, 2: numeric, 3: span */ + +directive_line_value( + unique int id: @directive_line ref, + int line: int ref); + +directive_line_file( + unique int id: @directive_line ref, + int file: @file ref); + +directive_line_offset( + unique int id: @directive_line ref, + int offset: int ref); + +directive_line_span( + unique int id: @directive_line ref, + int startLine: int ref, + int startColumn: int ref, + int endLine: int ref, + int endColumn: int ref); + +directive_nullables( + unique int id: @directive_nullable, + int setting: int ref, /* 0: disable, 1: enable, 2: restore */ + int target: int ref); /* 0: none, 1: annotations, 2: warnings */ + +directive_warnings( + unique int id: @directive_warning, + string message: string ref); + +directive_errors( + unique int id: @directive_error, + string message: string ref); + +directive_undefines( + unique int id: @directive_undefine, + string name: string ref); + +directive_defines( + unique int id: @directive_define, + string name: string ref); + +pragma_checksums( + unique int id: @pragma_checksum, + int file: @file ref, + string guid: string ref, + string bytes: string ref); + +pragma_warnings( + unique int id: @pragma_warning, + int kind: int ref /* 0 = disable, 1 = restore */); + +#keyset[id, index] +pragma_warning_error_codes( + int id: @pragma_warning ref, + string errorCode: string ref, + int index: int ref); + +preprocessor_directive_location( + unique int id: @preprocessor_directive ref, + int loc: @location ref); + +preprocessor_directive_compilation( + int id: @preprocessor_directive ref, + int compilation: @compilation ref); + +preprocessor_directive_active( + unique int id: @preprocessor_directive ref, + int active: int ref); /* 0: false, 1: true */ + +/** TYPES **/ + +types( + unique int id: @type, + int kind: int ref, + string name: string ref); + +case @type.kind of + 1 = @bool_type +| 2 = @char_type +| 3 = @decimal_type +| 4 = @sbyte_type +| 5 = @short_type +| 6 = @int_type +| 7 = @long_type +| 8 = @byte_type +| 9 = @ushort_type +| 10 = @uint_type +| 11 = @ulong_type +| 12 = @float_type +| 13 = @double_type +| 14 = @enum_type +| 15 = @struct_type +| 17 = @class_type +| 19 = @interface_type +| 20 = @delegate_type +| 21 = @null_type +| 22 = @type_parameter +| 23 = @pointer_type +| 24 = @nullable_type +| 25 = @array_type +| 26 = @void_type +| 27 = @int_ptr_type +| 28 = @uint_ptr_type +| 29 = @dynamic_type +| 30 = @arglist_type +| 31 = @unknown_type +| 32 = @tuple_type +| 33 = @function_pointer_type +| 34 = @inline_array_type +| 35 = @extension_type + ; + +@simple_type = @bool_type | @char_type | @integral_type | @floating_point_type | @decimal_type; +@integral_type = @signed_integral_type | @unsigned_integral_type; +@signed_integral_type = @sbyte_type | @short_type | @int_type | @long_type; +@unsigned_integral_type = @byte_type | @ushort_type | @uint_type | @ulong_type; +@floating_point_type = @float_type | @double_type; +@value_type = @simple_type | @enum_type | @struct_type | @nullable_type | @int_ptr_type + | @uint_ptr_type | @tuple_type | @void_type | @inline_array_type; +@ref_type = @class_type | @interface_type | @array_type | @delegate_type | @null_type + | @dynamic_type | @extension_type; +@value_or_ref_type = @value_type | @ref_type; + +typerefs( + unique int id: @typeref, + string name: string ref); + +typeref_type( + int id: @typeref ref, + unique int typeId: @type ref); + +@type_or_ref = @type | @typeref; + +array_element_type( + unique int array: @array_type ref, + int dimension: int ref, + int rank: int ref, + int element: @type_or_ref ref); + +nullable_underlying_type( + unique int nullable: @nullable_type ref, + int underlying: @type_or_ref ref); + +pointer_referent_type( + unique int pointer: @pointer_type ref, + int referent: @type_or_ref ref); + +enum_underlying_type( + unique int enum_id: @enum_type ref, + int underlying_type_id: @type_or_ref ref); + +delegate_return_type( + unique int delegate_id: @delegate_type ref, + int return_type_id: @type_or_ref ref); + +function_pointer_return_type( + unique int function_pointer_id: @function_pointer_type ref, + int return_type_id: @type_or_ref ref); + +extension_receiver_type( + unique int extension: @extension_type ref, + int receiver_type_id: @type_or_ref ref); + +extend( + int sub: @type ref, + int super: @type_or_ref ref); + +anonymous_types( + unique int id: @type ref); + +@interface_or_ref = @interface_type | @typeref; + +implement( + int sub: @type ref, + int super: @type_or_ref ref); + +type_location( + int id: @type ref, + int loc: @location ref); + +tuple_underlying_type( + unique int tuple: @tuple_type ref, + int struct: @type_or_ref ref); + +#keyset[tuple, index] +tuple_element( + int tuple: @tuple_type ref, + int index: int ref, + unique int field: @field ref); + +attributes( + unique int id: @attribute, + int kind: int ref, + int type_id: @type_or_ref ref, + int target: @attributable ref); + +case @attribute.kind of + 0 = @attribute_default +| 1 = @attribute_return +| 2 = @attribute_assembly +| 3 = @attribute_module +; + +attribute_location( + int id: @attribute ref, + int loc: @location ref); + +@type_mention_parent = @element | @type_mention; + +type_mention( + unique int id: @type_mention, + int type_id: @type_or_ref ref, + int parent: @type_mention_parent ref); + +type_mention_location( + unique int id: @type_mention ref, + int loc: @location ref); + +@has_type_annotation = @assignable | @type_parameter | @callable | @expr | @delegate_type | @generic | @function_pointer_type; + +/** + * A direct annotation on an entity, for example `string? x;`. + * + * Annotations: + * 2 = reftype is not annotated "!" + * 3 = reftype is annotated "?" + * 4 = readonly ref type / in parameter + * 5 = ref type parameter, return or local variable + * 6 = out parameter + * + * Note that the annotation depends on the element it annotates. + * @assignable: The annotation is on the type of the assignable, for example the variable type. + * @type_parameter: The annotation is on the reftype constraint + * @callable: The annotation is on the return type + * @array_type: The annotation is on the element type + */ +type_annotation(int id: @has_type_annotation ref, int annotation: int ref); + +nullability(unique int nullability: @nullability, int kind: int ref); + +case @nullability.kind of + 0 = @oblivious +| 1 = @not_annotated +| 2 = @annotated +; + +#keyset[parent, index] +nullability_parent(int nullability: @nullability ref, int index: int ref, int parent: @nullability ref) + +type_nullability(int id: @has_type_annotation ref, int nullability: @nullability ref); + +/** + * The nullable flow state of an expression, as determined by Roslyn. + * 0 = none (default, not populated) + * 1 = not null + * 2 = maybe null + */ +expr_flowstate(unique int id: @expr ref, int state: int ref); + +/** GENERICS **/ + +@generic = @type | @method | @local_function; + +type_parameters( + unique int id: @type_parameter ref, + int index: int ref, + int generic_id: @generic ref, + int variance: int ref /* none = 0, out = 1, in = 2 */); + +#keyset[constructed_id, index] +type_arguments( + int id: @type_or_ref ref, + int index: int ref, + int constructed_id: @generic_or_ref ref); + +@generic_or_ref = @generic | @typeref; + +constructed_generic( + unique int constructed: @generic ref, + int generic: @generic_or_ref ref); + +type_parameter_constraints( + unique int id: @type_parameter_constraints, + int param_id: @type_parameter ref); + +type_parameter_constraints_location( + int id: @type_parameter_constraints ref, + int loc: @location ref); + +general_type_parameter_constraints( + int id: @type_parameter_constraints ref, + int kind: int ref /* class = 1, struct = 2, new = 3 */); + +specific_type_parameter_constraints( + int id: @type_parameter_constraints ref, + int base_id: @type_or_ref ref); + +specific_type_parameter_nullability( + int id: @type_parameter_constraints ref, + int base_id: @type_or_ref ref, + int nullability: @nullability ref); + +/** FUNCTION POINTERS */ + +function_pointer_calling_conventions( + int id: @function_pointer_type ref, + int kind: int ref); + +#keyset[id, index] +has_unmanaged_calling_conventions( + int id: @function_pointer_type ref, + int index: int ref, + int conv_id: @type_or_ref ref); + +/** MODIFIERS */ + +@modifiable = @modifiable_direct | @event_accessor; + +@modifiable_direct = @member | @accessor | @local_function | @anonymous_function_expr; + +modifiers( + unique int id: @modifier, + string name: string ref); + +has_modifiers( + int id: @modifiable_direct ref, + int mod_id: @modifier ref); + +/** MEMBERS **/ + +@member = @method | @constructor | @destructor | @field | @property | @event | @operator | @indexer | @type; + +@named_exprorstmt = @goto_stmt | @labeled_stmt | @expr; + +@virtualizable = @method | @property | @indexer | @event | @operator; + +exprorstmt_name( + unique int parent_id: @named_exprorstmt ref, + string name: string ref); + +nested_types( + unique int id: @type ref, + int declaring_type_id: @type ref, + int unbound_id: @type ref); + +properties( + unique int id: @property, + string name: string ref, + int declaring_type_id: @type ref, + int type_id: @type_or_ref ref, + int unbound_id: @property ref); + +property_location( + int id: @property ref, + int loc: @location ref); + +indexers( + unique int id: @indexer, + string name: string ref, + int declaring_type_id: @type ref, + int type_id: @type_or_ref ref, + int unbound_id: @indexer ref); + +indexer_location( + int id: @indexer ref, + int loc: @location ref); + +accessors( + unique int id: @accessor, + int kind: int ref, + string name: string ref, + int declaring_member_id: @member ref, + int unbound_id: @accessor ref); + +case @accessor.kind of + 1 = @getter +| 2 = @setter + ; + +init_only_accessors( + unique int id: @accessor ref); + +accessor_location( + int id: @accessor ref, + int loc: @location ref); + +events( + unique int id: @event, + string name: string ref, + int declaring_type_id: @type ref, + int type_id: @type_or_ref ref, + int unbound_id: @event ref); + +event_location( + int id: @event ref, + int loc: @location ref); + +event_accessors( + unique int id: @event_accessor, + int kind: int ref, + string name: string ref, + int declaring_event_id: @event ref, + int unbound_id: @event_accessor ref); + +case @event_accessor.kind of + 1 = @add_event_accessor +| 2 = @remove_event_accessor + ; + +event_accessor_location( + int id: @event_accessor ref, + int loc: @location ref); + +operators( + unique int id: @operator, + string name: string ref, + string symbol: string ref, + int declaring_type_id: @type ref, + int type_id: @type_or_ref ref, + int unbound_id: @operator ref); + +operator_location( + int id: @operator ref, + int loc: @location ref); + +constant_value( + int id: @variable ref, + string value: string ref); + +/** CALLABLES **/ + +@callable = @method | @constructor | @destructor | @operator | @callable_accessor | @anonymous_function_expr | @local_function; + +@callable_accessor = @accessor | @event_accessor; + +methods( + unique int id: @method, + string name: string ref, + int declaring_type_id: @type ref, + int type_id: @type_or_ref ref, + int unbound_id: @method ref); + +method_location( + int id: @method ref, + int loc: @location ref); + +constructors( + unique int id: @constructor, + string name: string ref, + int declaring_type_id: @type ref, + int unbound_id: @constructor ref); + +constructor_location( + int id: @constructor ref, + int loc: @location ref); + +destructors( + unique int id: @destructor, + string name: string ref, + int declaring_type_id: @type ref, + int unbound_id: @destructor ref); + +destructor_location( + int id: @destructor ref, + int loc: @location ref); + +overrides( + int id: @callable ref, + int base_id: @callable ref); + +explicitly_implements( + int id: @member ref, + int interface_id: @interface_or_ref ref); + +local_functions( + unique int id: @local_function, + string name: string ref, + int return_type: @type ref, + int unbound_id: @local_function ref); + +local_function_stmts( + unique int fn: @local_function_stmt ref, + int stmt: @local_function ref); + +/** VARIABLES **/ + +@variable = @local_scope_variable | @field; + +@local_scope_variable = @local_variable | @parameter; + +fields( + unique int id: @field, + int kind: int ref, + string name: string ref, + int declaring_type_id: @type ref, + int type_id: @type_or_ref ref, + int unbound_id: @field ref); + +case @field.kind of + 1 = @addressable_field +| 2 = @constant + ; + +field_location( + int id: @field ref, + int loc: @location ref); + +localvars( + unique int id: @local_variable, + int kind: int ref, + string name: string ref, + int implicitly_typed: int ref /* 0 = no, 1 = yes */, + int type_id: @type_or_ref ref, + int parent_id: @local_var_decl_expr ref); + +case @local_variable.kind of + 1 = @addressable_local_variable +| 2 = @local_constant +| 3 = @local_variable_ref + ; + +localvar_location( + unique int id: @local_variable ref, + int loc: @location ref); + +@parameterizable = @callable | @delegate_type | @indexer | @function_pointer_type | @extension_type; + +#keyset[name, parent_id] +#keyset[index, parent_id] +params( + unique int id: @parameter, + string name: string ref, + int type_id: @type_or_ref ref, + int index: int ref, + int mode: int ref, /* value = 0, ref = 1, out = 2, params/array = 3, this = 4, in = 5, ref readonly = 6 */ + int parent_id: @parameterizable ref, + int unbound_id: @parameter ref); + +param_location( + int id: @parameter ref, + int loc: @location ref); + +@has_scoped_annotation = @local_scope_variable + +scoped_annotation( + int id: @has_scoped_annotation ref, + int kind: int ref // scoped ref = 1, scoped value = 2 + ); + +/** STATEMENTS **/ + +@exprorstmt_parent = @control_flow_element | @top_level_exprorstmt_parent; + +statements( + unique int id: @stmt, + int kind: int ref); + +#keyset[index, parent] +stmt_parent( + unique int stmt: @stmt ref, + int index: int ref, + int parent: @control_flow_element ref); + +@top_level_stmt_parent = @callable; + +// [index, parent] is not a keyset because the same parent may be compiled multiple times +stmt_parent_top_level( + unique int stmt: @stmt ref, + int index: int ref, + int parent: @top_level_stmt_parent ref); + +case @stmt.kind of + 1 = @block_stmt +| 2 = @expr_stmt +| 3 = @if_stmt +| 4 = @switch_stmt +| 5 = @while_stmt +| 6 = @do_stmt +| 7 = @for_stmt +| 8 = @foreach_stmt +| 9 = @break_stmt +| 10 = @continue_stmt +| 11 = @goto_stmt +| 12 = @goto_case_stmt +| 13 = @goto_default_stmt +| 14 = @throw_stmt +| 15 = @return_stmt +| 16 = @yield_stmt +| 17 = @try_stmt +| 18 = @checked_stmt +| 19 = @unchecked_stmt +| 20 = @lock_stmt +| 21 = @using_block_stmt +| 22 = @var_decl_stmt +| 23 = @const_decl_stmt +| 24 = @empty_stmt +| 25 = @unsafe_stmt +| 26 = @fixed_stmt +| 27 = @label_stmt +| 28 = @catch +| 29 = @case_stmt +| 30 = @local_function_stmt +| 31 = @using_decl_stmt + ; + +@using_stmt = @using_block_stmt | @using_decl_stmt; + +@labeled_stmt = @label_stmt | @case; + +@decl_stmt = @var_decl_stmt | @const_decl_stmt | @using_decl_stmt; + +@cond_stmt = @if_stmt | @switch_stmt; + +@loop_stmt = @while_stmt | @do_stmt | @for_stmt | @foreach_stmt; + +@jump_stmt = @break_stmt | @goto_any_stmt | @continue_stmt | @throw_stmt | @return_stmt + | @yield_stmt; + +@goto_any_stmt = @goto_default_stmt | @goto_case_stmt | @goto_stmt; + + +stmt_location( + unique int id: @stmt ref, + int loc: @location ref); + +catch_type( + unique int catch_id: @catch ref, + int type_id: @type_or_ref ref, + int kind: int ref /* explicit = 1, implicit = 2 */); + +foreach_stmt_info( + unique int id: @foreach_stmt ref, + int kind: int ref /* non-async = 1, async = 2 */); + +@foreach_symbol = @method | @property | @type_or_ref; + +#keyset[id, kind] +foreach_stmt_desugar( + int id: @foreach_stmt ref, + int symbol: @foreach_symbol ref, + int kind: int ref /* GetEnumeratorMethod = 1, CurrentProperty = 2, MoveNextMethod = 3, DisposeMethod = 4, ElementType = 5 */); + +/** EXPRESSIONS **/ + +expressions( + unique int id: @expr, + int kind: int ref, + int type_id: @type_or_ref ref); + +#keyset[index, parent] +expr_parent( + unique int expr: @expr ref, + int index: int ref, + int parent: @control_flow_element ref); + +@top_level_expr_parent = @attribute | @field | @property | @indexer | @parameter | @directive_if | @directive_elif; + +@top_level_exprorstmt_parent = @top_level_expr_parent | @top_level_stmt_parent; + +// [index, parent] is not a keyset because the same parent may be compiled multiple times +expr_parent_top_level( + unique int expr: @expr ref, + int index: int ref, + int parent: @top_level_exprorstmt_parent ref); + +case @expr.kind of +/* literal */ + 1 = @bool_literal_expr +| 2 = @char_literal_expr +| 3 = @decimal_literal_expr +| 4 = @int_literal_expr +| 5 = @long_literal_expr +| 6 = @uint_literal_expr +| 7 = @ulong_literal_expr +| 8 = @float_literal_expr +| 9 = @double_literal_expr +| 10 = @utf16_string_literal_expr +| 11 = @null_literal_expr +/* primary & unary */ +| 12 = @this_access_expr +| 13 = @base_access_expr +| 14 = @local_variable_access_expr +| 15 = @parameter_access_expr +| 16 = @field_access_expr +| 17 = @property_access_expr +| 18 = @method_access_expr +| 19 = @event_access_expr +| 20 = @indexer_access_expr +| 21 = @array_access_expr +| 22 = @type_access_expr +| 23 = @typeof_expr +| 24 = @method_invocation_expr +| 25 = @delegate_invocation_expr +| 26 = @operator_invocation_expr +| 27 = @cast_expr +| 28 = @object_creation_expr +| 29 = @explicit_delegate_creation_expr +| 30 = @implicit_delegate_creation_expr +| 31 = @array_creation_expr +| 32 = @default_expr +| 33 = @plus_expr +| 34 = @minus_expr +| 35 = @bit_not_expr +| 36 = @log_not_expr +| 37 = @post_incr_expr +| 38 = @post_decr_expr +| 39 = @pre_incr_expr +| 40 = @pre_decr_expr +/* multiplicative */ +| 41 = @mul_expr +| 42 = @div_expr +| 43 = @rem_expr +/* additive */ +| 44 = @add_expr +| 45 = @sub_expr +/* shift */ +| 46 = @lshift_expr +| 47 = @rshift_expr +/* relational */ +| 48 = @lt_expr +| 49 = @gt_expr +| 50 = @le_expr +| 51 = @ge_expr +/* equality */ +| 52 = @eq_expr +| 53 = @ne_expr +/* logical */ +| 54 = @bit_and_expr +| 55 = @bit_xor_expr +| 56 = @bit_or_expr +| 57 = @log_and_expr +| 58 = @log_or_expr +/* type testing */ +| 59 = @is_expr +| 60 = @as_expr +/* null coalescing */ +| 61 = @null_coalescing_expr +/* conditional */ +| 62 = @conditional_expr +/* assignment */ +| 63 = @simple_assign_expr +| 64 = @assign_add_expr +| 65 = @assign_sub_expr +| 66 = @assign_mul_expr +| 67 = @assign_div_expr +| 68 = @assign_rem_expr +| 69 = @assign_and_expr +| 70 = @assign_xor_expr +| 71 = @assign_or_expr +| 72 = @assign_lshift_expr +| 73 = @assign_rshift_expr +/* more */ +| 74 = @object_init_expr +| 75 = @collection_init_expr +| 76 = @array_init_expr +| 77 = @checked_expr +| 78 = @unchecked_expr +| 79 = @constructor_init_expr +| 80 = @add_event_expr +| 81 = @remove_event_expr +| 82 = @par_expr +| 83 = @local_var_decl_expr +| 84 = @lambda_expr +| 85 = @anonymous_method_expr +| 86 = @namespace_expr +/* dynamic */ +| 92 = @dynamic_element_access_expr +| 93 = @dynamic_member_access_expr +/* unsafe */ +| 100 = @pointer_indirection_expr +| 101 = @address_of_expr +| 102 = @sizeof_expr +/* async */ +| 103 = @await_expr +/* C# 6.0 */ +| 104 = @nameof_expr +| 105 = @interpolated_string_expr +| 106 = @unknown_expr +/* C# 7.0 */ +| 107 = @throw_expr +| 108 = @tuple_expr +| 109 = @local_function_invocation_expr +| 110 = @ref_expr +| 111 = @discard_expr +/* C# 8.0 */ +| 112 = @range_expr +| 113 = @index_expr +| 114 = @switch_expr +| 115 = @recursive_pattern_expr +| 116 = @property_pattern_expr +| 117 = @positional_pattern_expr +| 118 = @switch_case_expr +| 119 = @assign_coalesce_expr +| 120 = @suppress_nullable_warning_expr +| 121 = @namespace_access_expr +/* C# 9.0 */ +| 122 = @lt_pattern_expr +| 123 = @gt_pattern_expr +| 124 = @le_pattern_expr +| 125 = @ge_pattern_expr +| 126 = @not_pattern_expr +| 127 = @and_pattern_expr +| 128 = @or_pattern_expr +| 129 = @function_pointer_invocation_expr +| 130 = @with_expr +/* C# 11.0 */ +| 131 = @list_pattern_expr +| 132 = @slice_pattern_expr +| 133 = @urshift_expr +| 134 = @assign_urshift_expr +| 135 = @utf8_string_literal_expr +/* C# 12.0 */ +| 136 = @collection_expr +| 137 = @spread_element_expr +| 138 = @interpolated_string_insert_expr +/* Preprocessor */ +| 999 = @define_symbol_expr +; + +@switch = @switch_stmt | @switch_expr; +@case = @case_stmt | @switch_case_expr; +@pattern_match = @case | @is_expr; +@unary_pattern_expr = @not_pattern_expr; +@relational_pattern_expr = @gt_pattern_expr | @lt_pattern_expr | @ge_pattern_expr | @le_pattern_expr; +@binary_pattern_expr = @and_pattern_expr | @or_pattern_expr; + +@integer_literal_expr = @int_literal_expr | @long_literal_expr | @uint_literal_expr | @ulong_literal_expr; +@real_literal_expr = @float_literal_expr | @double_literal_expr | @decimal_literal_expr; +@string_literal_expr = @utf16_string_literal_expr | @utf8_string_literal_expr; +@literal_expr = @bool_literal_expr | @char_literal_expr | @integer_literal_expr | @real_literal_expr + | @string_literal_expr | @null_literal_expr; + +@assign_expr = @simple_assign_expr | @assign_op_expr | @local_var_decl_expr; +@assign_op_call_expr = @assign_arith_expr | @assign_bitwise_expr +@assign_op_expr = @assign_op_call_expr | @assign_event_expr | @assign_coalesce_expr; +@assign_event_expr = @add_event_expr | @remove_event_expr; + +@add_operation = @add_expr | @assign_add_expr; +@sub_operation = @sub_expr | @assign_sub_expr; +@mul_operation = @mul_expr | @assign_mul_expr; +@div_operation = @div_expr | @assign_div_expr; +@rem_operation = @rem_expr | @assign_rem_expr; +@and_operation = @bit_and_expr | @assign_and_expr; +@xor_operation = @bit_xor_expr | @assign_xor_expr; +@or_operation = @bit_or_expr | @assign_or_expr; +@lshift_operation = @lshift_expr | @assign_lshift_expr; +@rshift_operation = @rshift_expr | @assign_rshift_expr; +@urshift_operation = @urshift_expr | @assign_urshift_expr; +@null_coalescing_operation = @null_coalescing_expr | @assign_coalesce_expr; + +@assign_arith_expr = @assign_add_expr | @assign_sub_expr | @assign_mul_expr | @assign_div_expr + | @assign_rem_expr +@assign_bitwise_expr = @assign_and_expr | @assign_or_expr | @assign_xor_expr + | @assign_lshift_expr | @assign_rshift_expr | @assign_urshift_expr; + +@member_access_expr = @field_access_expr | @property_access_expr | @indexer_access_expr | @event_access_expr + | @method_access_expr | @type_access_expr | @dynamic_member_access_expr; +@access_expr = @member_access_expr | @this_access_expr | @base_access_expr | @assignable_access_expr | @namespace_access_expr; +@element_access_expr = @indexer_access_expr | @array_access_expr | @dynamic_element_access_expr; + +@local_variable_access = @local_variable_access_expr | @local_var_decl_expr; +@local_scope_variable_access_expr = @parameter_access_expr | @local_variable_access; +@variable_access_expr = @local_scope_variable_access_expr | @field_access_expr; + +@assignable_access_expr = @variable_access_expr | @property_access_expr | @element_access_expr + | @event_access_expr | @dynamic_member_access_expr; + +@objectorcollection_init_expr = @object_init_expr | @collection_init_expr; + +@delegate_creation_expr = @explicit_delegate_creation_expr | @implicit_delegate_creation_expr; + +@bin_arith_op_expr = @mul_expr | @div_expr | @rem_expr | @add_expr | @sub_expr; +@incr_op_expr = @pre_incr_expr | @post_incr_expr; +@decr_op_expr = @pre_decr_expr | @post_decr_expr; +@mut_op_expr = @incr_op_expr | @decr_op_expr; +@un_arith_op_expr = @plus_expr | @minus_expr | @mut_op_expr; +@arith_op_expr = @bin_arith_op_expr | @un_arith_op_expr; + +@ternary_log_op_expr = @conditional_expr; +@bin_log_op_expr = @log_and_expr | @log_or_expr | @null_coalescing_expr; +@un_log_op_expr = @log_not_expr; +@log_expr = @un_log_op_expr | @bin_log_op_expr | @ternary_log_op_expr; + +@bin_bit_op_expr = @bit_and_expr | @bit_or_expr | @bit_xor_expr | @lshift_expr + | @rshift_expr | @urshift_expr; +@un_bit_op_expr = @bit_not_expr; +@bit_expr = @un_bit_op_expr | @bin_bit_op_expr; + +@equality_op_expr = @eq_expr | @ne_expr; +@rel_op_expr = @gt_expr | @lt_expr| @ge_expr | @le_expr; +@comp_expr = @equality_op_expr | @rel_op_expr; + +@op_expr = @un_op | @bin_op | @ternary_op; + +@ternary_op = @ternary_log_op_expr; +@bin_op = @assign_expr | @bin_arith_op_expr | @bin_log_op_expr | @bin_bit_op_expr | @comp_expr; +@un_op = @un_arith_op_expr | @un_log_op_expr | @un_bit_op_expr | @sizeof_expr + | @pointer_indirection_expr | @address_of_expr; + +@anonymous_function_expr = @lambda_expr | @anonymous_method_expr; + +@op_invoke_expr = @operator_invocation_expr | @assign_op_call_expr +@call = @method_invocation_expr | @constructor_init_expr | @op_invoke_expr + | @delegate_invocation_expr | @object_creation_expr | @call_access_expr + | @local_function_invocation_expr | @function_pointer_invocation_expr; + +@call_access_expr = @property_access_expr | @event_access_expr | @indexer_access_expr; + +@late_bindable_expr = @dynamic_element_access_expr | @dynamic_member_access_expr + | @object_creation_expr | @method_invocation_expr | @op_invoke_expr; + +@throw_element = @throw_expr | @throw_stmt; + +@implicitly_typeable_object_creation_expr = @object_creation_expr | @explicit_delegate_creation_expr; + +implicitly_typed_array_creation( + unique int id: @array_creation_expr ref); + +explicitly_sized_array_creation( + unique int id: @array_creation_expr ref); + +stackalloc_array_creation( + unique int id: @array_creation_expr ref); + +implicitly_typed_object_creation( + unique int id: @implicitly_typeable_object_creation_expr ref); + +mutator_invocation_mode( + unique int id: @operator_invocation_expr ref, + int mode: int ref /* prefix = 1, postfix = 2*/); + +expr_value( + unique int id: @expr ref, + string value: string ref); + +expr_call( + unique int caller_id: @expr ref, + int target_id: @callable ref); + +expr_access( + unique int accesser_id: @access_expr ref, + int target_id: @accessible ref); + +@accessible = @method | @assignable | @local_function | @namespace; + +expr_location( + unique int id: @expr ref, + int loc: @location ref); + +dynamic_member_name( + unique int id: @late_bindable_expr ref, + string name: string ref); + +@qualifiable_expr = @member_access_expr + | @method_invocation_expr + | @element_access_expr + | @assign_op_call_expr; + +conditional_access( + unique int id: @qualifiable_expr ref); + +expr_argument( + unique int id: @expr ref, + int mode: int ref); + /* mode is the same as params: value = 0, ref = 1, out = 2 */ + +expr_argument_name( + unique int id: @expr ref, + string name: string ref); + +lambda_expr_return_type( + unique int id: @lambda_expr ref, + int type_id: @type_or_ref ref); + +/* Compiler generated */ + +compiler_generated(unique int id: @element ref); + +/** CONTROL/DATA FLOW **/ + +@control_flow_element = @stmt | @expr | @parameter; + +/* XML Files */ + +xmlEncoding ( + unique int id: @file ref, + string encoding: string ref); + +xmlDTDs( + unique int id: @xmldtd, + string root: string ref, + string publicId: string ref, + string systemId: string ref, + int fileid: @file ref); + +xmlElements( + unique int id: @xmlelement, + string name: string ref, + int parentid: @xmlparent ref, + int idx: int ref, + int fileid: @file ref); + +xmlAttrs( + unique int id: @xmlattribute, + int elementid: @xmlelement ref, + string name: string ref, + string value: string ref, + int idx: int ref, + int fileid: @file ref); + +xmlNs( + int id: @xmlnamespace, + string prefixName: string ref, + string URI: string ref, + int fileid: @file ref); + +xmlHasNs( + int elementId: @xmlnamespaceable ref, + int nsId: @xmlnamespace ref, + int fileid: @file ref); + +xmlComments( + unique int id: @xmlcomment, + string text: string ref, + int parentid: @xmlparent ref, + int fileid: @file ref); + +xmlChars( + unique int id: @xmlcharacters, + string text: string ref, + int parentid: @xmlparent ref, + int idx: int ref, + int isCDATA: int ref, + int fileid: @file ref); + +@xmlparent = @file | @xmlelement; +@xmlnamespaceable = @xmlelement | @xmlattribute; + +xmllocations( + int xmlElement: @xmllocatable ref, + int location: @location_default ref); + +@xmllocatable = @xmlcharacters | @xmlelement | @xmlcomment | @xmlattribute | @xmldtd | @file | @xmlnamespace; + +/* Comments */ + +commentline( + unique int id: @commentline, + int kind: int ref, + string text: string ref, + string rawtext: string ref); + +case @commentline.kind of + 0 = @singlelinecomment +| 1 = @xmldoccomment +| 2 = @multilinecomment; + +commentline_location( + unique int id: @commentline ref, + int loc: @location ref); + +commentblock( + unique int id : @commentblock); + +commentblock_location( + unique int id: @commentblock ref, + int loc: @location ref); + +commentblock_binding( + int id: @commentblock ref, + int entity: @element ref, + int bindtype: int ref); /* 0: Parent, 1: Best, 2: Before, 3: After */ + +commentblock_child( + int id: @commentblock ref, + int commentline: @commentline ref, + int index: int ref); + +/* ASP.NET */ + +case @asp_element.kind of + 0=@asp_close_tag +| 1=@asp_code +| 2=@asp_comment +| 3=@asp_data_binding +| 4=@asp_directive +| 5=@asp_open_tag +| 6=@asp_quoted_string +| 7=@asp_text +| 8=@asp_xml_directive; + +@asp_attribute = @asp_code | @asp_data_binding | @asp_quoted_string; + +asp_elements( + unique int id: @asp_element, + int kind: int ref, + int loc: @location ref); + +asp_comment_server(unique int comment: @asp_comment ref); +asp_code_inline(unique int code: @asp_code ref); +asp_directive_attribute( + int directive: @asp_directive ref, + int index: int ref, + string name: string ref, + int value: @asp_quoted_string ref); +asp_directive_name( + unique int directive: @asp_directive ref, + string name: string ref); +asp_element_body( + unique int element: @asp_element ref, + string body: string ref); +asp_tag_attribute( + int tag: @asp_open_tag ref, + int index: int ref, + string name: string ref, + int attribute: @asp_attribute ref); +asp_tag_name( + unique int tag: @asp_open_tag ref, + string name: string ref); +asp_tag_isempty(int tag: @asp_open_tag ref); diff --git a/csharp/downgrades/d13c4c187d7318fd2b8f35c7e8d7f4dc26be68b1/upgrade.properties b/csharp/downgrades/d13c4c187d7318fd2b8f35c7e8d7f4dc26be68b1/upgrade.properties new file mode 100644 index 00000000000..85b8a1e6c23 --- /dev/null +++ b/csharp/downgrades/d13c4c187d7318fd2b8f35c7e8d7f4dc26be68b1/upgrade.properties @@ -0,0 +1,2 @@ +description: Restructure and rename types related to operations. +compatibility: full diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/ElementAccess.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/ElementAccess.cs index 345e691a8a8..b75b3e7d0d9 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/ElementAccess.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/ElementAccess.cs @@ -1,5 +1,6 @@ using System.IO; using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using Semmle.Extraction.Kinds; @@ -8,7 +9,7 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions internal abstract class ElementAccess : Expression { protected ElementAccess(ExpressionNodeInfo info, ExpressionSyntax qualifier, BracketedArgumentListSyntax argumentList) - : base(info.SetKind(GetKind(info.Context, qualifier))) + : base(info.SetKind(GetKind(info.Context, info.Node, qualifier))) { this.qualifier = qualifier; this.argumentList = argumentList; @@ -17,6 +18,125 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions private readonly ExpressionSyntax qualifier; private readonly BracketedArgumentListSyntax argumentList; + + private ISymbol? GetTargetSymbol() + { + return Context.GetSymbolInfo(base.Syntax).Symbol; + } + + private static void SetExprArgument(TextWriter trapFile, Expression left, Expression right) + { + trapFile.expr_argument(left, 0); + trapFile.expr_argument(right, 0); + } + + private Expression MakeZeroFromEndExpression(IExpressionParentEntity parent, int child) + { + var info = new ExpressionInfo( + Context, + AnnotatedTypeSymbol.CreateNotAnnotated(Context.Compilation.GetSpecialType(SpecialType.System_Int32)), + Location, + ExprKind.INDEX, + parent, + child, + isCompilerGenerated: true, + null); + + var index = new Expression(info); + + MakeZeroLiteral(index, 0); + return index; + } + + private Expression MakeZeroLiteral(IExpressionParentEntity parent, int child) + { + return Literal.CreateGenerated(Context, parent, child, Context.Compilation.GetSpecialType(SpecialType.System_Int32), 0, Location); + } + + + /// + /// It is assumed that either the input is + /// 1. A normal expression that can be used as endpoint (e.g a constant like "3"). + /// 2. An index expression indicating that we should read from the end (e.g "^1"). + /// + /// The syntax node representing the range endpoint. + /// The parent expression entity. + /// The child index within the parent. + /// An expression representing the endpoint of a range to be used in conjunction with a slice operation. + private Expression MakeFromRangeEndpoint(ExpressionSyntax syntax, IExpressionParentEntity parent, int child) + { + var info = new ExpressionNodeInfo(Context, syntax, parent, child); + + return syntax.Kind() == SyntaxKind.IndexExpression + ? PrefixUnary.Create(info.SetKind(ExprKind.INDEX)) + : Factory.Create(info); + } + + /// + /// Determines whether the given method is a slice method, which is defined as a method with + /// the name "Slice" or "Substring" and two parameters. + /// + /// The method symbol to check. + /// True if the method is a slice method; false otherwise. + private bool IsSlice(IMethodSymbol method, out RangeExpressionSyntax? range) + { + range = null; + + if (argumentList.Arguments.Count == 1) + { + range = argumentList.Arguments[0].Expression as RangeExpressionSyntax; + } + + return (method.Name == "Slice" || method.Name == "Substring") + && method.Parameters.Length == 2; + } + + /// + /// Populates a slice method call based on the given range. + /// Roslyn translates indexer accesses with range expressions in the following way. + /// 1. s[a..b] -> s.Slice(a, b - a) + /// 2. s[..b] -> s.Slice(0, b) + /// 3. s[a..] -> s.Slice(a, s.Length - a) + /// 4. s[..] -> s.Slice(0, s.Length) + /// However, it is possible that both the qualifier or the index endpoints may contain method calls. + /// If we want to translate this accurately, we would need to introduce synthetic statements for qualifier and + /// the endpoints, which should then be used in the slice method call. + /// To avoid this, we translate as follows. + /// 1. s[a..b] -> s.Slice(a, b) + /// 2. s[..b] -> s.Slice(0, b) + /// 3. s[a..] -> s.Slice(a, ^0) + /// 4. s[..] -> s.Slice(0, ^0) + /// + /// Even though index expressions can't technically be used in this way, they signal that we + /// could perceive ^b as "length - b". + /// + /// Call arguments are only populated when a range expression is directly available in + /// the list of arguments. + /// This means that cases like below are not handled. + /// System.Range x = 1..3; + /// s[x] + /// + /// The trap file to write to. + /// The slice method symbol. + /// The range expression syntax. + private void PopulateSlice(TextWriter trapFile, IMethodSymbol slice, RangeExpressionSyntax? range) + { + if (range is not null) + { + // Populate the call arguments + var left = range.LeftOperand is ExpressionSyntax lsyntax + ? MakeFromRangeEndpoint(lsyntax, this, 0) + : MakeZeroLiteral(this, 0); + + var right = range.RightOperand is ExpressionSyntax rsyntax + ? MakeFromRangeEndpoint(rsyntax, this, 1) + : MakeZeroFromEndExpression(this, 1); + + SetExprArgument(trapFile, left, right); + } + trapFile.expr_call(this, Method.Create(Context, slice)); + } + protected override void PopulateExpression(TextWriter trapFile) { if (Kind == ExprKind.POINTER_INDIRECTION) @@ -30,11 +150,19 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions else { Create(Context, qualifier, this, -1); + + var target = GetTargetSymbol(); + if (target is IMethodSymbol method && IsSlice(method, out var range)) + { + // When an indexer on a span or string is used in conjunction with a range expression, the compiler translates + // this into a call to the "Slice" or "Substring" method. + // In this case, we want to populate a slice/substring method call instead of an indexer access. + PopulateSlice(trapFile, method, range); + return; + } + PopulateArguments(trapFile, argumentList, 0); - - var symbolInfo = Context.GetSymbolInfo(base.Syntax); - - if (symbolInfo.Symbol is IPropertySymbol indexer) + if (target is IPropertySymbol { IsIndexer: true } indexer) { trapFile.expr_access(this, Indexer.Create(Context, indexer)); } @@ -46,8 +174,11 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions private static bool IsArray(ITypeSymbol symbol) => symbol.TypeKind == Microsoft.CodeAnalysis.TypeKind.Array || symbol.IsInlineArray(); - private static ExprKind GetKind(Context cx, ExpressionSyntax qualifier) + private static ExprKind GetKind(Context cx, ExpressionSyntax syntax, ExpressionSyntax qualifier) { + if (cx.GetSymbolInfo(syntax).Symbol is IMethodSymbol) + return ExprKind.METHOD_INVOCATION; + var qualifierType = cx.GetType(qualifier); // This is a compilation error, so make a guess and continue. diff --git a/csharp/ql/lib/change-notes/2026-05-21-spanaccess-range.md b/csharp/ql/lib/change-notes/2026-05-21-spanaccess-range.md new file mode 100644 index 00000000000..b5e81d9adb9 --- /dev/null +++ b/csharp/ql/lib/change-notes/2026-05-21-spanaccess-range.md @@ -0,0 +1,4 @@ +--- +category: minorAnalysis +--- +* Improved extraction of range-access expressions on spans and strings (for example, `a[0..3]`). These expressions are now extracted as `Slice` (span) or `Substring` (string) calls. diff --git a/csharp/ql/lib/change-notes/2026-05-22-property-indexer-partial-override.md b/csharp/ql/lib/change-notes/2026-05-22-property-indexer-partial-override.md new file mode 100644 index 00000000000..4be78a49c1f --- /dev/null +++ b/csharp/ql/lib/change-notes/2026-05-22-property-indexer-partial-override.md @@ -0,0 +1,4 @@ +--- +category: minorAnalysis +--- +* Improved property and indexer call target resolution for partially overridden properties and indexers. diff --git a/csharp/ql/lib/change-notes/2026-06-12-razor-page-handler-sources.md b/csharp/ql/lib/change-notes/2026-06-12-razor-page-handler-sources.md new file mode 100644 index 00000000000..aca9d7631cd --- /dev/null +++ b/csharp/ql/lib/change-notes/2026-06-12-razor-page-handler-sources.md @@ -0,0 +1,4 @@ +--- +category: majorAnalysis +--- +* Added Razor Page handler method parameters (e.g., `OnGet`, `OnPost`, `OnPostAsync`) as remote flow sources, enabling security queries such as `cs/sql-injection` to detect vulnerabilities in `PageModel` subclasses. diff --git a/csharp/ql/lib/change-notes/2026-06-12-restructure-operations.md b/csharp/ql/lib/change-notes/2026-06-12-restructure-operations.md new file mode 100644 index 00000000000..89459c5b981 --- /dev/null +++ b/csharp/ql/lib/change-notes/2026-06-12-restructure-operations.md @@ -0,0 +1,4 @@ +--- +category: breaking +--- +* Renamed types related to *operation* expressions. The QL classes `BinaryArithmeticOperation`, `BinaryBitwiseOperation`, and `BinaryLogicalOperation` now include compound assignments; for example, `BinaryArithmeticOperation` now includes `a += b`. diff --git a/csharp/ql/lib/experimental/code/csharp/Cryptography/NonCryptographicHashes.qll b/csharp/ql/lib/experimental/code/csharp/Cryptography/NonCryptographicHashes.qll index 130e563a663..ae4b4ad3f61 100644 --- a/csharp/ql/lib/experimental/code/csharp/Cryptography/NonCryptographicHashes.qll +++ b/csharp/ql/lib/experimental/code/csharp/Cryptography/NonCryptographicHashes.qll @@ -50,15 +50,15 @@ private predicate maybeUsedInElfHashFunction(Variable v, Operation xor, Operatio | add instanceof AddOperation and e1.getAChild*() = add.getAnOperand() and - e1 instanceof BinaryBitwiseOperation and - e2 = e1.(BinaryBitwiseOperation).getLeftOperand() and + e1 instanceof BinaryBitwiseExpr and + e2 = e1.(BinaryBitwiseExpr).getLeftOperand() and v = addAssign.getTargetVariable() and addAssign.getAChild*() = add and (xor instanceof BitwiseXorExpr or xor instanceof AssignXorExpr) and addAssign.getControlFlowNode().getASuccessor*() = xor.getControlFlowNode() and xorAssign.getAChild*() = xor and v = xorAssign.getTargetVariable() and - (notOp instanceof UnaryBitwiseOperation or notOp instanceof AssignBitwiseOperation) and + (notOp instanceof UnaryBitwiseOperation or notOp instanceof AssignBitwiseExpr) and xor.getControlFlowNode().getASuccessor*() = notOp.getControlFlowNode() and notAssign.getAChild*() = notOp and v = notAssign.getTargetVariable() and diff --git a/csharp/ql/lib/semmle/code/csharp/Assignable.qll b/csharp/ql/lib/semmle/code/csharp/Assignable.qll index 7bd432d48ce..89dc594ec3f 100644 --- a/csharp/ql/lib/semmle/code/csharp/Assignable.qll +++ b/csharp/ql/lib/semmle/code/csharp/Assignable.qll @@ -290,7 +290,7 @@ module AssignableInternal { newtype TAssignableDefinition = TAssignmentDefinition(Assignment a) { not a.getLeftOperand() instanceof TupleExpr and - not a instanceof AssignCallOperation and + not a instanceof AssignCallExpr and not a instanceof AssignCoalesceExpr } or TTupleAssignmentDefinition(AssignExpr ae, Expr leaf) { tupleAssignmentDefinition(ae, leaf) } or @@ -324,7 +324,7 @@ module AssignableInternal { TAddressOfDefinition(AddressOfExpr aoe) or TPatternDefinition(TopLevelPatternDecl tlpd) or TAssignOperationDefinition(AssignOperation ao) { - ao instanceof AssignCallOperation and not ao instanceof CompoundAssignmentOperatorCall + ao instanceof AssignCallExpr and not ao instanceof CompoundAssignmentOperatorCall or ao instanceof AssignCoalesceExpr } diff --git a/csharp/ql/lib/semmle/code/csharp/Property.qll b/csharp/ql/lib/semmle/code/csharp/Property.qll index c9a338d0359..3a007b0d6e9 100644 --- a/csharp/ql/lib/semmle/code/csharp/Property.qll +++ b/csharp/ql/lib/semmle/code/csharp/Property.qll @@ -57,6 +57,28 @@ class DeclarationWithGetSetAccessors extends DeclarationWithAccessors, TopLevelE /** Gets the `set` accessor of this declaration, if any. */ Setter getSetter() { result = this.getAnAccessor() } + /** Gets the target accessor of this declaration when used in a read context, if any. */ + Accessor getReadTarget() { + result = this.getGetter() + or + not exists(this.getGetter()) and + result = this.getOverridee().getReadTarget() + } + + /** Gets the target accessor of this declaration when used in a write context, if any. */ + Accessor getWriteTarget() { + result = this.getSetter() + or + not exists(this.getSetter()) and + result = this.getOverridee().getWriteTarget() + or + result = + any(Getter g | + g = this.getReadTarget() and + g.getAnnotatedReturnType().isRef() + ) + } + override DeclarationWithGetSetAccessors getOverridee() { result = DeclarationWithAccessors.super.getOverridee() } diff --git a/csharp/ql/lib/semmle/code/csharp/controlflow/Guards.qll b/csharp/ql/lib/semmle/code/csharp/controlflow/Guards.qll index 3353866e334..e252d855da6 100644 --- a/csharp/ql/lib/semmle/code/csharp/controlflow/Guards.qll +++ b/csharp/ql/lib/semmle/code/csharp/controlflow/Guards.qll @@ -912,18 +912,17 @@ module Internal { ) or // In C#, `null + 1` has type `int?` with value `null` - exists(BinaryOperation bo, Expr o | - bo instanceof BinaryArithmeticOperation or - bo instanceof AssignArithmeticOperation - | - result = bo and - bo.getAnOperand() = e and - bo.getAnOperand() = o and - // The other operand must be provably non-null in order - // for `only if` to hold - nonNullValueImplied(o) and - e != o - ) + result = + any(BinaryArithmeticOperation bao | + exists(Expr o | + bao.getAnOperand() = e and + bao.getAnOperand() = o and + // The other operand must be provably non-null in order + // for `only if` to hold + nonNullValueImplied(o) and + e != o + ) + ) } /** @@ -934,10 +933,10 @@ module Internal { any(QualifiableExpr qe | qe.isConditional() and result = qe.getQualifier() - ) or + ) + or // In C#, `null + 1` has type `int?` with value `null` - e = any(BinaryArithmeticOperation bao | result = bao.getAnOperand()) or - e = any(AssignArithmeticOperation aao | result = aao.getAnOperand()) + e = any(BinaryArithmeticOperation bao | result = bao.getAnOperand()) } deprecated predicate isGuard(Expr e, GuardValue val) { diff --git a/csharp/ql/lib/semmle/code/csharp/dispatch/Dispatch.qll b/csharp/ql/lib/semmle/code/csharp/dispatch/Dispatch.qll index 909ba3b9d42..c5541d5a705 100644 --- a/csharp/ql/lib/semmle/code/csharp/dispatch/Dispatch.qll +++ b/csharp/ql/lib/semmle/code/csharp/dispatch/Dispatch.qll @@ -124,9 +124,7 @@ private module Internal { TDispatchDynamicOperatorCall(DynamicOperatorCall doc) or TDispatchDynamicMemberAccess(DynamicMemberAccess dma) or TDispatchDynamicElementAccess(DynamicElementAccess dea) or - TDispatchDynamicEventAccess( - AssignArithmeticOperation aao, DynamicMemberAccess dma, string name - ) { + TDispatchDynamicEventAccess(AssignArithmeticExpr aao, DynamicMemberAccess dma, string name) { isPotentialEventCall(aao, dma, name) } or TDispatchDynamicObjectCreation(DynamicObjectCreation doc) or @@ -230,7 +228,7 @@ private module Internal { * accessor. */ private predicate isPotentialEventCall( - AssignArithmeticOperation aao, DynamicMemberAccess dma, string name + AssignArithmeticExpr aao, DynamicMemberAccess dma, string name ) { aao instanceof DynamicOperatorCall and dma = aao.getLeftOperand() and @@ -1397,9 +1395,7 @@ private module Internal { private class DispatchDynamicEventAccess extends DispatchReflectionOrDynamicCall, TDispatchDynamicEventAccess { - override AssignArithmeticOperation getCall() { - this = TDispatchDynamicEventAccess(result, _, _) - } + override AssignArithmeticExpr getCall() { this = TDispatchDynamicEventAccess(result, _, _) } override string getName() { this = TDispatchDynamicEventAccess(_, _, result) } diff --git a/csharp/ql/lib/semmle/code/csharp/exprs/ArithmeticOperation.qll b/csharp/ql/lib/semmle/code/csharp/exprs/ArithmeticOperation.qll index 193c48ed3a2..2b909ac1b99 100644 --- a/csharp/ql/lib/semmle/code/csharp/exprs/ArithmeticOperation.qll +++ b/csharp/ql/lib/semmle/code/csharp/exprs/ArithmeticOperation.qll @@ -11,19 +11,27 @@ import Expr * (`UnaryArithmeticOperation`) or a binary arithmetic operation * (`BinaryArithmeticOperation`). */ -class ArithmeticOperation extends Operation, @arith_op_expr { +class ArithmeticOperation extends Operation, @arith_operation { override string getOperator() { none() } } /** - * A unary arithmetic operation. Either a unary minus operation - * (`UnaryMinusExpr`), a unary plus operation (`UnaryPlusExpr`), - * or a mutator operation (`MutatorOperation`). + * A binary arithmetic operation. Either a binary arithmetic expression (`BinaryArithmeticExpr`) or + * an arithmetic assignment expression (`AssignArithmeticExpr`). */ -class UnaryArithmeticOperation extends ArithmeticOperation, UnaryOperation, @un_arith_op_expr { } +class BinaryArithmeticOperation extends ArithmeticOperation, BinaryOperation, @bin_arith_operation { + override string getOperator() { none() } +} /** - * A unary minus operation, for example `-x`. + * A unary arithmetic operation. Either a unary minus expression + * (`UnaryMinusExpr`), a unary plus expression (`UnaryPlusExpr`), + * or a mutator operation (`MutatorOperation`). + */ +class UnaryArithmeticOperation extends ArithmeticOperation, UnaryOperation, @un_arith_operation { } + +/** + * A unary minus expression, for example `-x`. */ class UnaryMinusExpr extends UnaryArithmeticOperation, @minus_expr { override string getOperator() { result = "-" } @@ -32,7 +40,7 @@ class UnaryMinusExpr extends UnaryArithmeticOperation, @minus_expr { } /** - * A unary plus operation, for example `+x`. + * A unary plus expression, for example `+x`. */ class UnaryPlusExpr extends UnaryArithmeticOperation, @plus_expr { override string getOperator() { result = "+" } @@ -44,40 +52,40 @@ class UnaryPlusExpr extends UnaryArithmeticOperation, @plus_expr { * A mutator operation. Either an increment operation (`IncrementOperation`) * or a decrement operation (`DecrementOperation`). */ -class MutatorOperation extends UnaryArithmeticOperation, @mut_op_expr { } +class MutatorOperation extends UnaryArithmeticOperation, @mut_operation { } /** - * An increment operation. Either a postfix increment operation - * (`PostIncrExpr`) or a prefix increment operation (`PreIncrExpr`). + * An increment operation. Either a postfix increment expression + * (`PostIncrExpr`) or a prefix increment expression (`PreIncrExpr`). */ -class IncrementOperation extends MutatorOperation, @incr_op_expr { +class IncrementOperation extends MutatorOperation, @incr_operation { override string getOperator() { result = "++" } } /** - * A decrement operation. Either a postfix decrement operation - * (`PostDecrExpr`) or a prefix decrement operation (`PreDecrExpr`). + * A decrement operation. Either a postfix decrement expression + * (`PostDecrExpr`) or a prefix decrement expression (`PreDecrExpr`). */ -class DecrementOperation extends MutatorOperation, @decr_op_expr { +class DecrementOperation extends MutatorOperation, @decr_operation { override string getOperator() { result = "--" } } /** - * A prefix increment operation, for example `++x`. + * A prefix increment expression, for example `++x`. */ class PreIncrExpr extends IncrementOperation, @pre_incr_expr { override string getAPrimaryQlClass() { result = "PreIncrExpr" } } /** - * A prefix decrement operation, for example `--x`. + * A prefix decrement expression, for example `--x`. */ class PreDecrExpr extends DecrementOperation, @pre_decr_expr { override string getAPrimaryQlClass() { result = "PreDecrExpr" } } /** - * A postfix increment operation, for example `x++`. + * A postfix increment expression, for example `x++`. */ class PostIncrExpr extends IncrementOperation, @post_incr_expr { override string toString() { result = "..." + this.getOperator() } @@ -86,7 +94,7 @@ class PostIncrExpr extends IncrementOperation, @post_incr_expr { } /** - * A postfix decrement operation, for example `x--`. + * A postfix decrement expression, for example `x--`. */ class PostDecrExpr extends DecrementOperation, @post_decr_expr { override string toString() { result = "..." + this.getOperator() } @@ -95,55 +103,84 @@ class PostDecrExpr extends DecrementOperation, @post_decr_expr { } /** - * A binary arithmetic operation. Either an addition operation - * (`AddExpr`), a subtraction operation (`SubExpr`), a multiplication - * operation (`MulExpr`), a division operation (`DivExpr`), or a - * remainder operation (`RemExpr`). + * An addition operation, either `x + y` or `x += y`. */ -class BinaryArithmeticOperation extends ArithmeticOperation, BinaryOperation, @bin_arith_op_expr { - override string getOperator() { none() } +class AddOperation extends BinaryArithmeticOperation, @add_operation { } + +/** + * A subtraction operation, either `x - y` or `x -= y`. + */ +class SubOperation extends BinaryArithmeticOperation, @sub_operation { } + +/** + * A multiplication operation, either `x * y` or `x *= y`. + */ +class MulOperation extends BinaryArithmeticOperation, @mul_operation { } + +/** + * A division operation, either `x / y` or `x /= y`. + */ +class DivOperation extends BinaryArithmeticOperation, @div_operation { + /** Gets the numerator of this division operation. */ + Expr getNumerator() { result = this.getLeftOperand() } + + /** Gets the denominator of this division operation. */ + Expr getDenominator() { result = this.getRightOperand() } } /** - * An addition operation, for example `x + y`. + * A remainder operation, either `x % y` or `x %= y`. */ -class AddExpr extends BinaryArithmeticOperation, AddOperation, @add_expr { +class RemOperation extends BinaryArithmeticOperation, @rem_operation { } + +/** + * A binary arithmetic expression. Either an addition expression + * (`AddExpr`), a subtraction expression (`SubExpr`), a multiplication + * expression (`MulExpr`), a division expression (`DivExpr`), or a + * remainder expression (`RemExpr`). + */ +class BinaryArithmeticExpr extends BinaryArithmeticOperation, @bin_arith_expr { } + +/** + * An addition expression, for example `x + y`. + */ +class AddExpr extends BinaryArithmeticExpr, AddOperation, @add_expr { override string getOperator() { result = "+" } override string getAPrimaryQlClass() { result = "AddExpr" } } /** - * A subtraction operation, for example `x - y`. + * A subtraction expression, for example `x - y`. */ -class SubExpr extends BinaryArithmeticOperation, SubOperation, @sub_expr { +class SubExpr extends BinaryArithmeticExpr, SubOperation, @sub_expr { override string getOperator() { result = "-" } override string getAPrimaryQlClass() { result = "SubExpr" } } /** - * A multiplication operation, for example `x * y`. + * A multiplication expression, for example `x * y`. */ -class MulExpr extends BinaryArithmeticOperation, MulOperation, @mul_expr { +class MulExpr extends BinaryArithmeticExpr, MulOperation, @mul_expr { override string getOperator() { result = "*" } override string getAPrimaryQlClass() { result = "MulExpr" } } /** - * A division operation, for example `x / y`. + * A division expression, for example `x / y`. */ -class DivExpr extends BinaryArithmeticOperation, DivOperation, @div_expr { +class DivExpr extends BinaryArithmeticExpr, DivOperation, @div_expr { override string getOperator() { result = "/" } override string getAPrimaryQlClass() { result = "DivExpr" } } /** - * A remainder operation, for example `x % y`. + * A remainder expression, for example `x % y`. */ -class RemExpr extends BinaryArithmeticOperation, RemOperation, @rem_expr { +class RemExpr extends BinaryArithmeticExpr, RemOperation, @rem_expr { override string getOperator() { result = "%" } override string getAPrimaryQlClass() { result = "RemExpr" } diff --git a/csharp/ql/lib/semmle/code/csharp/exprs/Assignment.qll b/csharp/ql/lib/semmle/code/csharp/exprs/Assignment.qll index f65b13bf8ec..cc31883c646 100644 --- a/csharp/ql/lib/semmle/code/csharp/exprs/Assignment.qll +++ b/csharp/ql/lib/semmle/code/csharp/exprs/Assignment.qll @@ -72,9 +72,9 @@ class AssignExpr extends Assignment, @simple_assign_expr { } /** - * An assignment operation. Either an arithmetic assignment operation - * (`AssignArithmeticOperation`), a bitwise assignment operation - * (`AssignBitwiseOperation`), an event assignment (`AddOrRemoveEventExpr`), or + * An assignment operation. Either an arithmetic assignment expression + * (`AssignArithmeticExpr`), a bitwise assignment expression + * (`AssignBitwiseExpr`), an event assignment (`AddOrRemoveEventExpr`), or * a null-coalescing assignment (`AssignCoalesceExpr`). */ class AssignOperation extends Assignment, @assign_op_expr { @@ -94,134 +94,147 @@ class AssignOperation extends Assignment, @assign_op_expr { } /** - * A compound assignment operation that invokes an operator. + * A compound assignment expression that invokes an operator. * * (1) `x += y` invokes the compound assignment operator `+=` (if it exists). * (2) `x += y` invokes the operator `+` and assigns `x + y` to `x`. * - * Either an arithmetic assignment operation (`AssignArithmeticOperation`) or a bitwise - * assignment operation (`AssignBitwiseOperation`). + * Either an arithmetic assignment expression (`AssignArithmeticExpr`) or a bitwise + * assignment expression (`AssignBitwiseExpr`). */ -class AssignCallOperation extends AssignOperation, OperatorCall, QualifiableExpr, - @assign_op_call_expr -{ +class AssignCallExpr extends AssignOperation, OperatorCall, QualifiableExpr, @assign_op_call_expr { override string toString() { result = AssignOperation.super.toString() } } /** - * An arithmetic assignment operation. Either an addition assignment operation - * (`AssignAddExpr`), a subtraction assignment operation (`AssignSubExpr`), a - * multiplication assignment operation (`AssignMulExpr`), a division assignment - * operation (`AssignDivExpr`), or a remainder assignment operation - * (`AssignRemExpr`). + * DEPRECATED: Use `AssignCallExpr` instead. */ -class AssignArithmeticOperation extends AssignCallOperation, @assign_arith_expr { } +deprecated class AssignCallOperation = AssignCallExpr; /** - * An addition assignment operation, for example `x += y`. + * An arithmetic assignment expression. Either an addition assignment expression + * (`AssignAddExpr`), a subtraction assignment expression (`AssignSubExpr`), a + * multiplication assignment expression (`AssignMulExpr`), a division assignment + * expression (`AssignDivExpr`), or a remainder assignment expression + * (`AssignRemExpr`). */ -class AssignAddExpr extends AssignArithmeticOperation, AddOperation, @assign_add_expr { +class AssignArithmeticExpr extends AssignCallExpr, @assign_arith_expr { } + +/** + * DEPRECATED: Use `AssignArithmeticExpr` instead. + */ +deprecated class AssignArithmeticOperation = AssignArithmeticExpr; + +/** + * An addition assignment expression, for example `x += y`. + */ +class AssignAddExpr extends AssignArithmeticExpr, AddOperation, @assign_add_expr { override string getOperator() { result = "+=" } override string getAPrimaryQlClass() { result = "AssignAddExpr" } } /** - * A subtraction assignment operation, for example `x -= y`. + * A subtraction assignment expression, for example `x -= y`. */ -class AssignSubExpr extends AssignArithmeticOperation, SubOperation, @assign_sub_expr { +class AssignSubExpr extends AssignArithmeticExpr, SubOperation, @assign_sub_expr { override string getOperator() { result = "-=" } override string getAPrimaryQlClass() { result = "AssignSubExpr" } } /** - * An multiplication assignment operation, for example `x *= y`. + * A multiplication assignment expression, for example `x *= y`. */ -class AssignMulExpr extends AssignArithmeticOperation, MulOperation, @assign_mul_expr { +class AssignMulExpr extends AssignArithmeticExpr, MulOperation, @assign_mul_expr { override string getOperator() { result = "*=" } override string getAPrimaryQlClass() { result = "AssignMulExpr" } } /** - * An division assignment operation, for example `x /= y`. + * A division assignment expression, for example `x /= y`. */ -class AssignDivExpr extends AssignArithmeticOperation, DivOperation, @assign_div_expr { +class AssignDivExpr extends AssignArithmeticExpr, DivOperation, @assign_div_expr { override string getOperator() { result = "/=" } override string getAPrimaryQlClass() { result = "AssignDivExpr" } } /** - * A remainder assignment operation, for example `x %= y`. + * A remainder assignment expression, for example `x %= y`. */ -class AssignRemExpr extends AssignArithmeticOperation, RemOperation, @assign_rem_expr { +class AssignRemExpr extends AssignArithmeticExpr, RemOperation, @assign_rem_expr { override string getOperator() { result = "%=" } override string getAPrimaryQlClass() { result = "AssignRemExpr" } } /** - * A bitwise assignment operation. Either a bitwise-and assignment - * operation (`AssignAndExpr`), a bitwise-or assignment - * operation (`AssignOrExpr`), a bitwise exclusive-or assignment - * operation (`AssignXorExpr`), a left-shift assignment - * operation (`AssignLeftShiftExpr`), or a right-shift assignment - * operation (`AssignRightShiftExpr`), or an unsigned right-shift assignment - * operation (`AssignUnsignedRightShiftExpr`). + * A bitwise assignment expression. Either a bitwise-and assignment + * expression (`AssignAndExpr`), a bitwise-or assignment + * expression (`AssignOrExpr`), a bitwise exclusive-or assignment + * expression (`AssignXorExpr`), a left-shift assignment + * expression (`AssignLeftShiftExpr`), or a right-shift assignment + * expression (`AssignRightShiftExpr`), or an unsigned right-shift assignment + * expression (`AssignUnsignedRightShiftExpr`). */ -class AssignBitwiseOperation extends AssignCallOperation, @assign_bitwise_expr { } +class AssignBitwiseExpr extends AssignCallExpr, @assign_bitwise_expr { } /** - * A bitwise-and assignment operation, for example `x &= y`. + * DEPRECATED: Use `AssignBitwiseExpr` instead. */ -class AssignAndExpr extends AssignBitwiseOperation, BitwiseAndOperation, @assign_and_expr { +deprecated class AssignBitwiseOperation = AssignBitwiseExpr; + +/** + * A bitwise-and assignment expression, for example `x &= y`. + */ +class AssignAndExpr extends AssignBitwiseExpr, BitwiseAndOperation, @assign_and_expr { override string getOperator() { result = "&=" } override string getAPrimaryQlClass() { result = "AssignAndExpr" } } /** - * A bitwise-or assignment operation, for example `x |= y`. + * A bitwise-or assignment expression, for example `x |= y`. */ -class AssignOrExpr extends AssignBitwiseOperation, BitwiseOrOperation, @assign_or_expr { +class AssignOrExpr extends AssignBitwiseExpr, BitwiseOrOperation, @assign_or_expr { override string getOperator() { result = "|=" } override string getAPrimaryQlClass() { result = "AssignOrExpr" } } /** - * A bitwise exclusive-or assignment operation, for example `x ^= y`. + * A bitwise exclusive-or assignment expression, for example `x ^= y`. */ -class AssignXorExpr extends AssignBitwiseOperation, BitwiseXorOperation, @assign_xor_expr { +class AssignXorExpr extends AssignBitwiseExpr, BitwiseXorOperation, @assign_xor_expr { override string getOperator() { result = "^=" } override string getAPrimaryQlClass() { result = "AssignXorExpr" } } /** - * A left-shift assignment operation, for example `x <<= y`. + * A left-shift assignment expression, for example `x <<= y`. */ -class AssignLeftShiftExpr extends AssignBitwiseOperation, LeftShiftOperation, @assign_lshift_expr { +class AssignLeftShiftExpr extends AssignBitwiseExpr, LeftShiftOperation, @assign_lshift_expr { override string getOperator() { result = "<<=" } override string getAPrimaryQlClass() { result = "AssignLeftShiftExpr" } } /** - * A right-shift assignment operation, for example `x >>= y`. + * A right-shift assignment expression, for example `x >>= y`. */ -class AssignRightShiftExpr extends AssignBitwiseOperation, RightShiftOperation, @assign_rshift_expr { +class AssignRightShiftExpr extends AssignBitwiseExpr, RightShiftOperation, @assign_rshift_expr { override string getOperator() { result = ">>=" } override string getAPrimaryQlClass() { result = "AssignRightShiftExpr" } } /** - * An unsigned right-shift assignment operation, for example `x >>>= y`. + * An unsigned right-shift assignment expression, for example `x >>>= y`. */ -class AssignUnsignedRightShiftExpr extends AssignBitwiseOperation, UnsignedRightShiftOperation, +class AssignUnsignedRightShiftExpr extends AssignBitwiseExpr, UnsignedRightShiftOperation, @assign_urshift_expr { override string getOperator() { result = ">>>=" } @@ -297,10 +310,10 @@ class RemoveEventExpr extends AddOrRemoveEventExpr, @remove_event_expr { } /** - * A null-coalescing assignment operation, for example `x ??= y`. + * A null-coalescing assignment expression, for example `x ??= y`. */ class AssignCoalesceExpr extends AssignOperation, NullCoalescingOperation, @assign_coalesce_expr { - override string toString() { result = "... ??= ..." } + override string getOperator() { result = "??=" } override string getAPrimaryQlClass() { result = "AssignCoalesceExpr" } } diff --git a/csharp/ql/lib/semmle/code/csharp/exprs/BitwiseOperation.qll b/csharp/ql/lib/semmle/code/csharp/exprs/BitwiseOperation.qll index 14bb3d74e2b..b6449f71a48 100644 --- a/csharp/ql/lib/semmle/code/csharp/exprs/BitwiseOperation.qll +++ b/csharp/ql/lib/semmle/code/csharp/exprs/BitwiseOperation.qll @@ -10,16 +10,16 @@ import Expr * A bitwise operation. Either a unary bitwise operation (`UnaryBitwiseOperation`) * or a binary bitwise operation (`BinaryBitwiseOperation`). */ -class BitwiseOperation extends Operation, @bit_expr { } +class BitwiseOperation extends Operation, @bit_operation { } /** * A unary bitwise operation, that is, a bitwise complement operation * (`ComplementExpr`). */ -class UnaryBitwiseOperation extends BitwiseOperation, UnaryOperation, @un_bit_op_expr { } +class UnaryBitwiseOperation extends BitwiseOperation, UnaryOperation, @un_bit_operation { } /** - * A bitwise complement operation, for example `~x`. + * A bitwise complement expression, for example `~x`. */ class ComplementExpr extends UnaryBitwiseOperation, @bit_not_expr { override string getOperator() { result = "~" } @@ -28,67 +28,101 @@ class ComplementExpr extends UnaryBitwiseOperation, @bit_not_expr { } /** - * A binary bitwise operation. Either a bitwise-and operation - * (`BitwiseAndExpr`), a bitwise-or operation (`BitwiseOrExpr`), - * a bitwise exclusive-or operation (`BitwiseXorExpr`), a left-shift - * operation (`LeftShiftExpr`), a right-shift operation (`RightShiftExpr`), - * or an unsigned right-shift operation (`UnsignedRightShiftExpr`). + * A binary bitwise operation. Either a binary bitwise expression (`BinaryBitwiseExpr`) or + * a bitwise assignment expression (`AssignBitwiseExpr`). */ -class BinaryBitwiseOperation extends BitwiseOperation, BinaryOperation, @bin_bit_op_expr { +class BinaryBitwiseOperation extends BitwiseOperation, BinaryOperation, @bin_bit_operation { override string getOperator() { none() } } /** - * A left-shift operation, for example `x << y`. + * A bitwise-and operation, either `x & y` or `x &= y`. */ -class LeftShiftExpr extends BinaryBitwiseOperation, LeftShiftOperation, @lshift_expr { +class BitwiseAndOperation extends BinaryBitwiseOperation, @and_operation { } + +/** + * A bitwise-or operation, either `x | y` or `x |= y`. + */ +class BitwiseOrOperation extends BinaryBitwiseOperation, @or_operation { } + +/** + * A bitwise exclusive-or operation, either `x ^ y` or `x ^= y`. + */ +class BitwiseXorOperation extends BinaryBitwiseOperation, @xor_operation { } + +/** + * A left-shift operation, either `x << y` or `x <<= y`. + */ +class LeftShiftOperation extends BinaryBitwiseOperation, @lshift_operation { } + +/** + * A right-shift operation, either `x >> y` or `x >>= y`. + */ +class RightShiftOperation extends BinaryBitwiseOperation, @rshift_operation { } + +/** + * An unsigned right-shift operation, either `x >>> y` or `x >>>= y`. + */ +class UnsignedRightShiftOperation extends BinaryBitwiseOperation, @urshift_operation { } + +/** + * A binary bitwise expression. Either a bitwise-and expression + * (`BitwiseAndExpr`), a bitwise-or expression (`BitwiseOrExpr`), + * a bitwise exclusive-or expression (`BitwiseXorExpr`), a left-shift + * expression (`LeftShiftExpr`), a right-shift expression (`RightShiftExpr`), + * or an unsigned right-shift expression (`UnsignedRightShiftExpr`). + */ +class BinaryBitwiseExpr extends BinaryBitwiseOperation, @bin_bit_expr { } + +/** + * A left-shift expression, for example `x << y`. + */ +class LeftShiftExpr extends BinaryBitwiseExpr, LeftShiftOperation, @lshift_expr { override string getOperator() { result = "<<" } override string getAPrimaryQlClass() { result = "LeftShiftExpr" } } /** - * A right-shift operation, for example `x >> y`. + * A right-shift expression, for example `x >> y`. */ -class RightShiftExpr extends BinaryBitwiseOperation, RightShiftOperation, @rshift_expr { +class RightShiftExpr extends BinaryBitwiseExpr, RightShiftOperation, @rshift_expr { override string getOperator() { result = ">>" } override string getAPrimaryQlClass() { result = "RightShiftExpr" } } /** - * An unsigned right-shift operation, for example `x >>> y`. + * An unsigned right-shift expression, for example `x >>> y`. */ -class UnsignedRightShiftExpr extends BinaryBitwiseOperation, UnsignedRightShiftOperation, - @urshift_expr -{ +class UnsignedRightShiftExpr extends BinaryBitwiseExpr, UnsignedRightShiftOperation, @urshift_expr { override string getOperator() { result = ">>>" } override string getAPrimaryQlClass() { result = "UnsignedRightShiftExpr" } } /** - * A bitwise-and operation, for example `x & y`. + * A bitwise-and expression, for example `x & y`. */ -class BitwiseAndExpr extends BinaryBitwiseOperation, BitwiseAndOperation, @bit_and_expr { +class BitwiseAndExpr extends BinaryBitwiseExpr, BitwiseAndOperation, @bit_and_expr { override string getOperator() { result = "&" } override string getAPrimaryQlClass() { result = "BitwiseAndExpr" } } /** - * A bitwise-or operation, for example `x | y`. + * A bitwise-or expression, for example `x | y`. */ -class BitwiseOrExpr extends BinaryBitwiseOperation, BitwiseOrOperation, @bit_or_expr { +class BitwiseOrExpr extends BinaryBitwiseExpr, BitwiseOrOperation, @bit_or_expr { override string getOperator() { result = "|" } override string getAPrimaryQlClass() { result = "BitwiseOrExpr" } } /** - * A bitwise exclusive-or operation, for example `x ^ y`. + * A bitwise exclusive-or expression, for example `x ^ y`. */ -class BitwiseXorExpr extends BinaryBitwiseOperation, BitwiseXorOperation, @bit_xor_expr { +class BitwiseXorExpr extends BinaryBitwiseExpr, BitwiseXorOperation, @bit_xor_expr { override string getOperator() { result = "^" } override string getAPrimaryQlClass() { result = "BitwiseXorExpr" } diff --git a/csharp/ql/lib/semmle/code/csharp/exprs/Call.qll b/csharp/ql/lib/semmle/code/csharp/exprs/Call.qll index a358e73970c..c39143bd3d7 100644 --- a/csharp/ql/lib/semmle/code/csharp/exprs/Call.qll +++ b/csharp/ql/lib/semmle/code/csharp/exprs/Call.qll @@ -609,7 +609,7 @@ class InstanceMutatorOperatorCall extends MutatorOperatorCall { * } * ``` */ -class CompoundAssignmentOperatorCall extends AssignCallOperation { +class CompoundAssignmentOperatorCall extends AssignCallExpr { CompoundAssignmentOperatorCall() { this.getTarget() instanceof CompoundAssignmentOperator } override Expr getArgument(int i) { result = this.getChildExpr(i + 1) and i >= 0 } @@ -762,20 +762,12 @@ class AccessorCall extends Call, QualifiableExpr, @call_access_expr { */ class PropertyCall extends AccessorCall, PropertyAccessExpr { override Accessor getReadTarget() { - this instanceof AssignableRead and result = this.getProperty().getGetter() + this instanceof AssignableRead and result = this.getProperty().getReadTarget() } override Accessor getWriteTarget() { this instanceof AssignableWrite and - exists(Property p | p = this.getProperty() | - result = p.getSetter() - or - result = - any(Getter g | - g = p.getGetter() and - g.getAnnotatedReturnType().isRef() - ) - ) + result = this.getProperty().getWriteTarget() } override Expr getArgument(int i) { @@ -806,20 +798,12 @@ class PropertyCall extends AccessorCall, PropertyAccessExpr { */ class IndexerCall extends AccessorCall, IndexerAccessExpr { override Accessor getReadTarget() { - this instanceof AssignableRead and result = this.getIndexer().getGetter() + this instanceof AssignableRead and result = this.getIndexer().getReadTarget() } override Accessor getWriteTarget() { this instanceof AssignableWrite and - exists(Indexer i | i = this.getIndexer() | - result = i.getSetter() - or - result = - any(Getter g | - g = i.getGetter() and - g.getAnnotatedReturnType().isRef() - ) - ) + result = this.getIndexer().getWriteTarget() } override Expr getArgument(int i) { diff --git a/csharp/ql/lib/semmle/code/csharp/exprs/Expr.qll b/csharp/ql/lib/semmle/code/csharp/exprs/Expr.qll index a26afb00490..857212f90aa 100644 --- a/csharp/ql/lib/semmle/code/csharp/exprs/Expr.qll +++ b/csharp/ql/lib/semmle/code/csharp/exprs/Expr.qll @@ -14,7 +14,6 @@ import Creation import Dynamic import Literal import LogicalOperation -import Operation import semmle.code.csharp.controlflow.ControlFlowElement import semmle.code.csharp.Location import semmle.code.csharp.Stmt @@ -212,7 +211,7 @@ class LocalConstantDeclExpr extends LocalVariableDeclExpr { * (`UnaryOperation`), a binary operation (`BinaryOperation`), or a * ternary operation (`TernaryOperation`). */ -class Operation extends Expr, @op_expr { +class Operation extends Expr, @operation_expr { /** Gets the name of the operator in this operation. */ string getOperator() { none() } @@ -227,7 +226,7 @@ class Operation extends Expr, @op_expr { * indirection operation (`PointerIndirectionExpr`), an address-of operation * (`AddressOfExpr`), or a unary logical operation (`UnaryLogicalOperation`). */ -class UnaryOperation extends Operation, @un_op { +class UnaryOperation extends Operation, @un_operation { /** Gets the operand of this unary operation. */ Expr getOperand() { result = this.getChild(0) } @@ -241,7 +240,7 @@ class UnaryOperation extends Operation, @un_op { * a binary logical operation (`BinaryLogicalOperation`), or an * assignment (`Assignment`). */ -class BinaryOperation extends Operation, @bin_op { +class BinaryOperation extends Operation, @bin_operation { /** Gets the left operand of this binary operation. */ Expr getLeftOperand() { result = this.getChild(0) } @@ -264,7 +263,7 @@ class BinaryOperation extends Operation, @bin_op { * A ternary operation, that is, a ternary conditional operation * (`ConditionalExpr`). */ -class TernaryOperation extends Operation, @ternary_op { } +class TernaryOperation extends Operation, @ternary_operation { } /** * A parenthesized expression, for example `(2 + 3)` in diff --git a/csharp/ql/lib/semmle/code/csharp/exprs/LogicalOperation.qll b/csharp/ql/lib/semmle/code/csharp/exprs/LogicalOperation.qll index 4161f734c9b..22b24202041 100644 --- a/csharp/ql/lib/semmle/code/csharp/exprs/LogicalOperation.qll +++ b/csharp/ql/lib/semmle/code/csharp/exprs/LogicalOperation.qll @@ -11,14 +11,14 @@ import Expr * a binary logical operation (`BinaryLogicalOperation`), or a ternary logical * operation (`TernaryLogicalOperation`). */ -class LogicalOperation extends Operation, @log_expr { +class LogicalOperation extends Operation, @log_operation { override string getOperator() { none() } } /** * A unary logical operation, that is, a logical 'not' (`LogicalNotExpr`). */ -class UnaryLogicalOperation extends LogicalOperation, UnaryOperation, @un_log_op_expr { } +class UnaryLogicalOperation extends LogicalOperation, UnaryOperation, @un_log_operation { } /** * A logical 'not', for example `!String.IsNullOrEmpty(s)`. @@ -31,10 +31,10 @@ class LogicalNotExpr extends UnaryLogicalOperation, @log_not_expr { /** * A binary logical operation. Either a logical 'and' (`LogicalAndExpr`), - * a logical 'or' (`LogicalAndExpr`), or a null-coalescing operation - * (`NullCoalescingExpr`). + * a logical 'or' (`LogicalOrExpr`), or a null-coalescing operation + * (`NullCoalescingOperation`). */ -class BinaryLogicalOperation extends LogicalOperation, BinaryOperation, @bin_log_op_expr { +class BinaryLogicalOperation extends LogicalOperation, BinaryOperation, @bin_log_operation { override string getOperator() { none() } } @@ -57,7 +57,12 @@ class LogicalOrExpr extends BinaryLogicalOperation, @log_or_expr { } /** - * A null-coalescing operation, for example `s ?? ""` on line 2 in + * A null-coalescing operation, either `x ?? y` or `x ??= y`. + */ +class NullCoalescingOperation extends BinaryLogicalOperation, @null_coalescing_operation { } + +/** + * A null-coalescing expression, for example `s ?? ""` on line 2 in * * ```csharp * string NonNullOrEmpty(string s) { @@ -65,9 +70,7 @@ class LogicalOrExpr extends BinaryLogicalOperation, @log_or_expr { * } * ``` */ -class NullCoalescingExpr extends BinaryLogicalOperation, NullCoalescingOperation, - @null_coalescing_expr -{ +class NullCoalescingExpr extends NullCoalescingOperation, @null_coalescing_expr { override string getOperator() { result = "??" } override string getAPrimaryQlClass() { result = "NullCoalescingExpr" } @@ -77,7 +80,7 @@ class NullCoalescingExpr extends BinaryLogicalOperation, NullCoalescingOperation * A ternary logical operation, that is, a ternary conditional expression * (`ConditionalExpr`). */ -class TernaryLogicalOperation extends LogicalOperation, TernaryOperation, @ternary_log_op_expr { } +class TernaryLogicalOperation extends LogicalOperation, TernaryOperation, @ternary_log_operation { } /** * A conditional expression, for example `s != null ? s.Length : -1` diff --git a/csharp/ql/lib/semmle/code/csharp/exprs/Operation.qll b/csharp/ql/lib/semmle/code/csharp/exprs/Operation.qll index 1f816baea86..19de7f20ee3 100644 --- a/csharp/ql/lib/semmle/code/csharp/exprs/Operation.qll +++ b/csharp/ql/lib/semmle/code/csharp/exprs/Operation.qll @@ -1,71 +1,6 @@ /** * Provides classes for operations that also have compound assignment forms. */ +deprecated module; import Expr - -/** - * An addition operation, either `x + y` or `x += y`. - */ -class AddOperation extends BinaryOperation, @add_operation { } - -/** - * A subtraction operation, either `x - y` or `x -= y`. - */ -class SubOperation extends BinaryOperation, @sub_operation { } - -/** - * A multiplication operation, either `x * y` or `x *= y`. - */ -class MulOperation extends BinaryOperation, @mul_operation { } - -/** - * A division operation, either `x / y` or `x /= y`. - */ -class DivOperation extends BinaryOperation, @div_operation { - /** Gets the numerator of this division operation. */ - Expr getNumerator() { result = this.getLeftOperand() } - - /** Gets the denominator of this division operation. */ - Expr getDenominator() { result = this.getRightOperand() } -} - -/** - * A remainder operation, either `x % y` or `x %= y`. - */ -class RemOperation extends BinaryOperation, @rem_operation { } - -/** - * A bitwise-and operation, either `x & y` or `x &= y`. - */ -class BitwiseAndOperation extends BinaryOperation, @and_operation { } - -/** - * A bitwise-or operation, either `x | y` or `x |= y`. - */ -class BitwiseOrOperation extends BinaryOperation, @or_operation { } - -/** - * A bitwise exclusive-or operation, either `x ^ y` or `x ^= y`. - */ -class BitwiseXorOperation extends BinaryOperation, @xor_operation { } - -/** - * A left-shift operation, either `x << y` or `x <<= y`. - */ -class LeftShiftOperation extends BinaryOperation, @lshift_operation { } - -/** - * A right-shift operation, either `x >> y` or `x >>= y`. - */ -class RightShiftOperation extends BinaryOperation, @rshift_operation { } - -/** - * An unsigned right-shift operation, either `x >>> y` or `x >>>= y`. - */ -class UnsignedRightShiftOperation extends BinaryOperation, @urshift_operation { } - -/** - * A null-coalescing operation, either `x ?? y` or `x ??= y`. - */ -class NullCoalescingOperation extends BinaryOperation, @null_coalescing_operation { } diff --git a/csharp/ql/lib/semmle/code/csharp/security/dataflow/flowsources/Remote.qll b/csharp/ql/lib/semmle/code/csharp/security/dataflow/flowsources/Remote.qll index aa8c8536556..68c06a1828d 100644 --- a/csharp/ql/lib/semmle/code/csharp/security/dataflow/flowsources/Remote.qll +++ b/csharp/ql/lib/semmle/code/csharp/security/dataflow/flowsources/Remote.qll @@ -13,6 +13,7 @@ private import semmle.code.csharp.frameworks.system.web.ui.WebControls private import semmle.code.csharp.frameworks.WCF private import semmle.code.csharp.frameworks.microsoft.Owin private import semmle.code.csharp.frameworks.microsoft.AspNetCore +private import semmle.code.csharp.frameworks.Razor private import semmle.code.csharp.dataflow.internal.ExternalFlow private import semmle.code.csharp.security.dataflow.flowsources.FlowSources @@ -314,6 +315,22 @@ class AspNetCoreActionMethodParameter extends AspNetCoreRemoteFlowSource, DataFl override string getSourceType() { result = "ASP.NET Core MVC action method parameter" } } +/** A parameter to a Razor Page handler method, viewed as a source of remote user input. */ +class AspNetCorePageHandlerMethodParameter extends AspNetCoreRemoteFlowSource, + DataFlow::ParameterNode +{ + AspNetCorePageHandlerMethodParameter() { + exists(Parameter p | + p = this.getParameter() and + p.fromSource() + | + p = any(PageModelClass pm).getAHandlerMethod().getAParameter() + ) + } + + override string getSourceType() { result = "ASP.NET Core Razor Page handler method parameter" } +} + private class ExternalRemoteFlowSource extends RemoteFlowSource { ExternalRemoteFlowSource() { sourceNode(this, "remote") } diff --git a/csharp/ql/lib/semmlecode.csharp.dbscheme b/csharp/ql/lib/semmlecode.csharp.dbscheme index 3cabc77473c..d13c4c187d7 100644 --- a/csharp/ql/lib/semmlecode.csharp.dbscheme +++ b/csharp/ql/lib/semmlecode.csharp.dbscheme @@ -1254,33 +1254,39 @@ case @expr.kind of @delegate_creation_expr = @explicit_delegate_creation_expr | @implicit_delegate_creation_expr; -@bin_arith_op_expr = @mul_expr | @div_expr | @rem_expr | @add_expr | @sub_expr; -@incr_op_expr = @pre_incr_expr | @post_incr_expr; -@decr_op_expr = @pre_decr_expr | @post_decr_expr; -@mut_op_expr = @incr_op_expr | @decr_op_expr; -@un_arith_op_expr = @plus_expr | @minus_expr | @mut_op_expr; -@arith_op_expr = @bin_arith_op_expr | @un_arith_op_expr; +@bin_arith_expr = @mul_expr | @div_expr | @rem_expr | @add_expr | @sub_expr; +@bin_arith_operation = @mul_operation | @div_operation | @rem_operation | @add_operation | @sub_operation; -@ternary_log_op_expr = @conditional_expr; -@bin_log_op_expr = @log_and_expr | @log_or_expr | @null_coalescing_expr; -@un_log_op_expr = @log_not_expr; -@log_expr = @un_log_op_expr | @bin_log_op_expr | @ternary_log_op_expr; +@incr_operation = @pre_incr_expr | @post_incr_expr; +@decr_operation = @pre_decr_expr | @post_decr_expr; +@mut_operation = @incr_operation | @decr_operation; +@un_arith_operation = @plus_expr | @minus_expr | @mut_operation; +@arith_operation = @bin_arith_operation | @un_arith_operation; -@bin_bit_op_expr = @bit_and_expr | @bit_or_expr | @bit_xor_expr | @lshift_expr - | @rshift_expr | @urshift_expr; -@un_bit_op_expr = @bit_not_expr; -@bit_expr = @un_bit_op_expr | @bin_bit_op_expr; +@ternary_log_operation = @conditional_expr; +@bin_log_operation = @log_and_expr | @log_or_expr | @null_coalescing_operation; +@un_log_operation = @log_not_expr; +@log_operation = @un_log_operation | @bin_log_operation | @ternary_log_operation; + +@bin_bit_expr = @bit_and_expr | @bit_or_expr | @bit_xor_expr | @lshift_expr + | @rshift_expr | @urshift_expr; +@bin_bit_operation = @and_operation | @or_operation | @xor_operation | @lshift_operation + | @rshift_operation | @urshift_operation; +@un_bit_expr = @bit_not_expr; +@un_bit_operation = @un_bit_expr; +@bit_expr = @un_bit_expr | @bin_bit_expr; +@bit_operation = @un_bit_operation | @bin_bit_operation; @equality_op_expr = @eq_expr | @ne_expr; @rel_op_expr = @gt_expr | @lt_expr| @ge_expr | @le_expr; @comp_expr = @equality_op_expr | @rel_op_expr; -@op_expr = @un_op | @bin_op | @ternary_op; +@operation_expr = @un_operation | @bin_operation | @ternary_operation; -@ternary_op = @ternary_log_op_expr; -@bin_op = @assign_expr | @bin_arith_op_expr | @bin_log_op_expr | @bin_bit_op_expr | @comp_expr; -@un_op = @un_arith_op_expr | @un_log_op_expr | @un_bit_op_expr | @sizeof_expr - | @pointer_indirection_expr | @address_of_expr; +@ternary_operation = @ternary_log_operation; +@bin_operation = @assign_expr | @bin_arith_operation | @bin_log_operation | @bin_bit_operation | @comp_expr; +@un_operation = @un_arith_operation | @un_log_operation | @un_bit_operation | @sizeof_expr + | @pointer_indirection_expr | @address_of_expr; @anonymous_function_expr = @lambda_expr | @anonymous_method_expr; diff --git a/csharp/ql/lib/upgrades/3cabc77473cbbda95edebafea345c2e3fdfa12d9/old.dbscheme b/csharp/ql/lib/upgrades/3cabc77473cbbda95edebafea345c2e3fdfa12d9/old.dbscheme new file mode 100644 index 00000000000..3cabc77473c --- /dev/null +++ b/csharp/ql/lib/upgrades/3cabc77473cbbda95edebafea345c2e3fdfa12d9/old.dbscheme @@ -0,0 +1,1505 @@ +/* This is a dummy line to alter the dbscheme, so we can make a database upgrade + * without actually changing any of the dbscheme predicates. It contains a date + * to allow for such updates in the future as well. + * + * 2021-07-14 + * + * DO NOT remove this comment carelessly, since it can revert the dbscheme back to a + * previously seen state (matching a previously seen SHA), which would make the upgrade + * mechanism not work properly. + */ + +/** + * An invocation of the compiler. Note that more than one file may be + * compiled per invocation. For example, this command compiles three + * source files: + * + * csc f1.cs f2.cs f3.cs + * + * The `id` simply identifies the invocation, while `cwd` is the working + * directory from which the compiler was invoked. + */ +compilations( + unique int id : @compilation, + string cwd : string ref +); + +compilation_info( + int id : @compilation ref, + string info_key: string ref, + string info_value: string ref +) + +/** + * The arguments that were passed to the extractor for a compiler + * invocation. If `id` is for the compiler invocation + * + * csc f1.cs f2.cs f3.cs + * + * then typically there will be rows for + * + * num | arg + * --- | --- + * 0 | --compiler + * 1 | *path to compiler* + * 2 | f1.cs + * 3 | f2.cs + * 4 | f3.cs + */ +#keyset[id, num] +compilation_args( + int id : @compilation ref, + int num : int ref, + string arg : string ref +); + +/** + * The expanded arguments that were passed to the extractor for a + * compiler invocation. This is similar to `compilation_args`, but + * for a `@someFile.rsp` argument, it includes the arguments from that + * file, rather than just taking the argument literally. + */ +#keyset[id, num] +compilation_expanded_args( + int id : @compilation ref, + int num : int ref, + string arg : string ref +); + +/** + * The source files that are compiled by a compiler invocation. + * If `id` is for the compiler invocation + * + * csc f1.cs f2.cs f3.cs + * + * then there will be rows for + * + * num | arg + * --- | --- + * 0 | f1.cs + * 1 | f2.cs + * 2 | f3.cs + */ +#keyset[id, num] +compilation_compiling_files( + int id : @compilation ref, + int num : int ref, + int file : @file ref +); + +/** + * The references used by a compiler invocation. + * If `id` is for the compiler invocation + * + * csc f1.cs f2.cs f3.cs /r:ref1.dll /r:ref2.dll /r:ref3.dll + * + * then there will be rows for + * + * num | arg + * --- | --- + * 0 | ref1.dll + * 1 | ref2.dll + * 2 | ref3.dll + */ +#keyset[id, num] +compilation_referencing_files( + int id : @compilation ref, + int num : int ref, + int file : @file ref +); + +/** + * The time taken by the extractor for a compiler invocation. + * + * For each file `num`, there will be rows for + * + * kind | seconds + * ---- | --- + * 1 | CPU seconds used by the extractor frontend + * 2 | Elapsed seconds during the extractor frontend + * 3 | CPU seconds used by the extractor backend + * 4 | Elapsed seconds during the extractor backend + */ +#keyset[id, num, kind] +compilation_time( + int id : @compilation ref, + int num : int ref, + /* kind: + 1 = frontend_cpu_seconds + 2 = frontend_elapsed_seconds + 3 = extractor_cpu_seconds + 4 = extractor_elapsed_seconds + */ + int kind : int ref, + float seconds : float ref +); + +/** + * An error or warning generated by the extractor. + * The diagnostic message `diagnostic` was generated during compiler + * invocation `compilation`, and is the `file_number_diagnostic_number`th + * message generated while extracting the `file_number`th file of that + * invocation. + */ +#keyset[compilation, file_number, file_number_diagnostic_number] +diagnostic_for( + unique int diagnostic : @diagnostic ref, + int compilation : @compilation ref, + int file_number : int ref, + int file_number_diagnostic_number : int ref +); + +diagnostics( + unique int id: @diagnostic, + int severity: int ref, + string error_tag: string ref, + string error_message: string ref, + string full_error_message: string ref, + int location: @location ref +); + +extractor_messages( + unique int id: @extractor_message, + int severity: int ref, + string origin : string ref, + string text : string ref, + string entity : string ref, + int location: @location ref, + string stack_trace : string ref +); + +/** + * If extraction was successful, then `cpu_seconds` and + * `elapsed_seconds` are the CPU time and elapsed time (respectively) + * that extraction took for compiler invocation `id`. + */ +compilation_finished( + unique int id : @compilation ref, + float cpu_seconds : float ref, + float elapsed_seconds : float ref +); + +compilation_assembly( + unique int id : @compilation ref, + int assembly: @assembly ref +) + +// Populated by the CSV extractor +externalData( + int id: @externalDataElement, + string path: string ref, + int column: int ref, + string value: string ref); + +sourceLocationPrefix( + string prefix: string ref); + +/* + * Overlay support + */ + +/** + * The CLI will automatically emit the tuple `databaseMetadata("isOverlay", "true")`, + * along with an `overlayChangedFiles` tuple for each new/modified/deleted file, + * when building an overlay database, and these can be used by the discard predicates. + */ +databaseMetadata( + string metadataKey : string ref, + string value : string ref +); + +overlayChangedFiles( + string path : string ref +); + +/* + * C# dbscheme + */ + +/** ELEMENTS **/ + +@element = @declaration | @stmt | @expr | @modifier | @attribute | @namespace_declaration + | @using_directive | @type_parameter_constraints | @externalDataElement + | @xmllocatable | @asp_element | @namespace | @preprocessor_directive; + +@declaration = @callable | @generic | @assignable | @namespace; + +@named_element = @namespace | @declaration; + +@declaration_with_accessors = @property | @indexer | @event; + +@assignable = @variable | @assignable_with_accessors | @event; + +@assignable_with_accessors = @property | @indexer; + +@attributable = @assembly | @field | @parameter | @operator | @method | @constructor + | @destructor | @callable_accessor | @value_or_ref_type | @declaration_with_accessors + | @local_function | @lambda_expr; + +/** LOCATIONS, ASEMMBLIES, MODULES, FILES and FOLDERS **/ + +@location = @location_default | @assembly; + +@locatable = @declaration_with_accessors | @callable_accessor | @declaration_or_directive + | @diagnostic | @extractor_message | @preprocessor_directive | @attribute | @type_mention | @type_parameter_constraints + | @declaration_with_accessors | @callable_accessor | @operator | @method + | @constructor | @destructor | @field | @local_variable | @parameter | @stmt | @expr + | @xmllocatable | @commentline | @commentblock | @asp_element + +locations_default( + unique int id: @location_default, + int file: @file ref, + int beginLine: int ref, + int beginColumn: int ref, + int endLine: int ref, + int endColumn: int ref); + +locations_mapped( + unique int id: @location_default ref, + int mapped_to: @location_default ref); + +@sourceline = @file | @callable | @xmllocatable; + +numlines( + int element_id: @sourceline ref, + int num_lines: int ref, + int num_code: int ref, + int num_comment: int ref); + +assemblies( + unique int id: @assembly, + int file: @file ref, + string fullname: string ref, + string name: string ref, + string version: string ref); + +files( + unique int id: @file, + string name: string ref); + +folders( + unique int id: @folder, + string name: string ref); + +@container = @folder | @file ; + +containerparent( + int parent: @container ref, + unique int child: @container ref); + +file_extraction_mode( + unique int file: @file ref, + int mode: int ref + /* 0 = normal, 1 = standalone extractor */ + ); + +/** NAMESPACES **/ + +@type_container = @namespace | @type; + +namespaces( + unique int id: @namespace, + string name: string ref); + +namespace_declarations( + unique int id: @namespace_declaration, + int namespace_id: @namespace ref); + +namespace_declaration_location( + unique int id: @namespace_declaration ref, + int loc: @location ref); + +parent_namespace( + unique int child_id: @type_container ref, + int namespace_id: @namespace ref); + +@declaration_or_directive = @namespace_declaration | @type | @using_directive; + +parent_namespace_declaration( + int child_id: @declaration_or_directive ref, // cannot be unique because of partial classes + int namespace_id: @namespace_declaration ref); + +@using_directive = @using_namespace_directive | @using_static_directive; + +using_global( + unique int id: @using_directive ref +); + +using_namespace_directives( + unique int id: @using_namespace_directive, + int namespace_id: @namespace ref); + +using_static_directives( + unique int id: @using_static_directive, + int type_id: @type_or_ref ref); + +using_directive_location( + unique int id: @using_directive ref, + int loc: @location ref); + +@preprocessor_directive = @pragma_warning | @pragma_checksum | @directive_define | @directive_undefine | @directive_warning + | @directive_error | @directive_nullable | @directive_line | @directive_region | @directive_endregion | @directive_if + | @directive_elif | @directive_else | @directive_endif; + +@conditional_directive = @directive_if | @directive_elif; +@branch_directive = @directive_if | @directive_elif | @directive_else; + +directive_ifs( + unique int id: @directive_if, + int branchTaken: int ref, /* 0: false, 1: true */ + int conditionValue: int ref); /* 0: false, 1: true */ + +directive_elifs( + unique int id: @directive_elif, + int branchTaken: int ref, /* 0: false, 1: true */ + int conditionValue: int ref, /* 0: false, 1: true */ + int parent: @directive_if ref, + int index: int ref); + +directive_elses( + unique int id: @directive_else, + int branchTaken: int ref, /* 0: false, 1: true */ + int parent: @directive_if ref, + int index: int ref); + +#keyset[id, start] +directive_endifs( + unique int id: @directive_endif, + unique int start: @directive_if ref); + +directive_define_symbols( + unique int id: @define_symbol_expr ref, + string name: string ref); + +directive_regions( + unique int id: @directive_region, + string name: string ref); + +#keyset[id, start] +directive_endregions( + unique int id: @directive_endregion, + unique int start: @directive_region ref); + +directive_lines( + unique int id: @directive_line, + int kind: int ref); /* 0: default, 1: hidden, 2: numeric, 3: span */ + +directive_line_value( + unique int id: @directive_line ref, + int line: int ref); + +directive_line_file( + unique int id: @directive_line ref, + int file: @file ref); + +directive_line_offset( + unique int id: @directive_line ref, + int offset: int ref); + +directive_line_span( + unique int id: @directive_line ref, + int startLine: int ref, + int startColumn: int ref, + int endLine: int ref, + int endColumn: int ref); + +directive_nullables( + unique int id: @directive_nullable, + int setting: int ref, /* 0: disable, 1: enable, 2: restore */ + int target: int ref); /* 0: none, 1: annotations, 2: warnings */ + +directive_warnings( + unique int id: @directive_warning, + string message: string ref); + +directive_errors( + unique int id: @directive_error, + string message: string ref); + +directive_undefines( + unique int id: @directive_undefine, + string name: string ref); + +directive_defines( + unique int id: @directive_define, + string name: string ref); + +pragma_checksums( + unique int id: @pragma_checksum, + int file: @file ref, + string guid: string ref, + string bytes: string ref); + +pragma_warnings( + unique int id: @pragma_warning, + int kind: int ref /* 0 = disable, 1 = restore */); + +#keyset[id, index] +pragma_warning_error_codes( + int id: @pragma_warning ref, + string errorCode: string ref, + int index: int ref); + +preprocessor_directive_location( + unique int id: @preprocessor_directive ref, + int loc: @location ref); + +preprocessor_directive_compilation( + int id: @preprocessor_directive ref, + int compilation: @compilation ref); + +preprocessor_directive_active( + unique int id: @preprocessor_directive ref, + int active: int ref); /* 0: false, 1: true */ + +/** TYPES **/ + +types( + unique int id: @type, + int kind: int ref, + string name: string ref); + +case @type.kind of + 1 = @bool_type +| 2 = @char_type +| 3 = @decimal_type +| 4 = @sbyte_type +| 5 = @short_type +| 6 = @int_type +| 7 = @long_type +| 8 = @byte_type +| 9 = @ushort_type +| 10 = @uint_type +| 11 = @ulong_type +| 12 = @float_type +| 13 = @double_type +| 14 = @enum_type +| 15 = @struct_type +| 17 = @class_type +| 19 = @interface_type +| 20 = @delegate_type +| 21 = @null_type +| 22 = @type_parameter +| 23 = @pointer_type +| 24 = @nullable_type +| 25 = @array_type +| 26 = @void_type +| 27 = @int_ptr_type +| 28 = @uint_ptr_type +| 29 = @dynamic_type +| 30 = @arglist_type +| 31 = @unknown_type +| 32 = @tuple_type +| 33 = @function_pointer_type +| 34 = @inline_array_type +| 35 = @extension_type + ; + +@simple_type = @bool_type | @char_type | @integral_type | @floating_point_type | @decimal_type; +@integral_type = @signed_integral_type | @unsigned_integral_type; +@signed_integral_type = @sbyte_type | @short_type | @int_type | @long_type; +@unsigned_integral_type = @byte_type | @ushort_type | @uint_type | @ulong_type; +@floating_point_type = @float_type | @double_type; +@value_type = @simple_type | @enum_type | @struct_type | @nullable_type | @int_ptr_type + | @uint_ptr_type | @tuple_type | @void_type | @inline_array_type; +@ref_type = @class_type | @interface_type | @array_type | @delegate_type | @null_type + | @dynamic_type | @extension_type; +@value_or_ref_type = @value_type | @ref_type; + +typerefs( + unique int id: @typeref, + string name: string ref); + +typeref_type( + int id: @typeref ref, + unique int typeId: @type ref); + +@type_or_ref = @type | @typeref; + +array_element_type( + unique int array: @array_type ref, + int dimension: int ref, + int rank: int ref, + int element: @type_or_ref ref); + +nullable_underlying_type( + unique int nullable: @nullable_type ref, + int underlying: @type_or_ref ref); + +pointer_referent_type( + unique int pointer: @pointer_type ref, + int referent: @type_or_ref ref); + +enum_underlying_type( + unique int enum_id: @enum_type ref, + int underlying_type_id: @type_or_ref ref); + +delegate_return_type( + unique int delegate_id: @delegate_type ref, + int return_type_id: @type_or_ref ref); + +function_pointer_return_type( + unique int function_pointer_id: @function_pointer_type ref, + int return_type_id: @type_or_ref ref); + +extension_receiver_type( + unique int extension: @extension_type ref, + int receiver_type_id: @type_or_ref ref); + +extend( + int sub: @type ref, + int super: @type_or_ref ref); + +anonymous_types( + unique int id: @type ref); + +@interface_or_ref = @interface_type | @typeref; + +implement( + int sub: @type ref, + int super: @type_or_ref ref); + +type_location( + int id: @type ref, + int loc: @location ref); + +tuple_underlying_type( + unique int tuple: @tuple_type ref, + int struct: @type_or_ref ref); + +#keyset[tuple, index] +tuple_element( + int tuple: @tuple_type ref, + int index: int ref, + unique int field: @field ref); + +attributes( + unique int id: @attribute, + int kind: int ref, + int type_id: @type_or_ref ref, + int target: @attributable ref); + +case @attribute.kind of + 0 = @attribute_default +| 1 = @attribute_return +| 2 = @attribute_assembly +| 3 = @attribute_module +; + +attribute_location( + int id: @attribute ref, + int loc: @location ref); + +@type_mention_parent = @element | @type_mention; + +type_mention( + unique int id: @type_mention, + int type_id: @type_or_ref ref, + int parent: @type_mention_parent ref); + +type_mention_location( + unique int id: @type_mention ref, + int loc: @location ref); + +@has_type_annotation = @assignable | @type_parameter | @callable | @expr | @delegate_type | @generic | @function_pointer_type; + +/** + * A direct annotation on an entity, for example `string? x;`. + * + * Annotations: + * 2 = reftype is not annotated "!" + * 3 = reftype is annotated "?" + * 4 = readonly ref type / in parameter + * 5 = ref type parameter, return or local variable + * 6 = out parameter + * + * Note that the annotation depends on the element it annotates. + * @assignable: The annotation is on the type of the assignable, for example the variable type. + * @type_parameter: The annotation is on the reftype constraint + * @callable: The annotation is on the return type + * @array_type: The annotation is on the element type + */ +type_annotation(int id: @has_type_annotation ref, int annotation: int ref); + +nullability(unique int nullability: @nullability, int kind: int ref); + +case @nullability.kind of + 0 = @oblivious +| 1 = @not_annotated +| 2 = @annotated +; + +#keyset[parent, index] +nullability_parent(int nullability: @nullability ref, int index: int ref, int parent: @nullability ref) + +type_nullability(int id: @has_type_annotation ref, int nullability: @nullability ref); + +/** + * The nullable flow state of an expression, as determined by Roslyn. + * 0 = none (default, not populated) + * 1 = not null + * 2 = maybe null + */ +expr_flowstate(unique int id: @expr ref, int state: int ref); + +/** GENERICS **/ + +@generic = @type | @method | @local_function; + +type_parameters( + unique int id: @type_parameter ref, + int index: int ref, + int generic_id: @generic ref, + int variance: int ref /* none = 0, out = 1, in = 2 */); + +#keyset[constructed_id, index] +type_arguments( + int id: @type_or_ref ref, + int index: int ref, + int constructed_id: @generic_or_ref ref); + +@generic_or_ref = @generic | @typeref; + +constructed_generic( + unique int constructed: @generic ref, + int generic: @generic_or_ref ref); + +type_parameter_constraints( + unique int id: @type_parameter_constraints, + int param_id: @type_parameter ref); + +type_parameter_constraints_location( + int id: @type_parameter_constraints ref, + int loc: @location ref); + +general_type_parameter_constraints( + int id: @type_parameter_constraints ref, + int kind: int ref /* class = 1, struct = 2, new = 3 */); + +specific_type_parameter_constraints( + int id: @type_parameter_constraints ref, + int base_id: @type_or_ref ref); + +specific_type_parameter_nullability( + int id: @type_parameter_constraints ref, + int base_id: @type_or_ref ref, + int nullability: @nullability ref); + +/** FUNCTION POINTERS */ + +function_pointer_calling_conventions( + int id: @function_pointer_type ref, + int kind: int ref); + +#keyset[id, index] +has_unmanaged_calling_conventions( + int id: @function_pointer_type ref, + int index: int ref, + int conv_id: @type_or_ref ref); + +/** MODIFIERS */ + +@modifiable = @modifiable_direct | @event_accessor; + +@modifiable_direct = @member | @accessor | @local_function | @anonymous_function_expr; + +modifiers( + unique int id: @modifier, + string name: string ref); + +has_modifiers( + int id: @modifiable_direct ref, + int mod_id: @modifier ref); + +/** MEMBERS **/ + +@member = @method | @constructor | @destructor | @field | @property | @event | @operator | @indexer | @type; + +@named_exprorstmt = @goto_stmt | @labeled_stmt | @expr; + +@virtualizable = @method | @property | @indexer | @event | @operator; + +exprorstmt_name( + unique int parent_id: @named_exprorstmt ref, + string name: string ref); + +nested_types( + unique int id: @type ref, + int declaring_type_id: @type ref, + int unbound_id: @type ref); + +properties( + unique int id: @property, + string name: string ref, + int declaring_type_id: @type ref, + int type_id: @type_or_ref ref, + int unbound_id: @property ref); + +property_location( + int id: @property ref, + int loc: @location ref); + +indexers( + unique int id: @indexer, + string name: string ref, + int declaring_type_id: @type ref, + int type_id: @type_or_ref ref, + int unbound_id: @indexer ref); + +indexer_location( + int id: @indexer ref, + int loc: @location ref); + +accessors( + unique int id: @accessor, + int kind: int ref, + string name: string ref, + int declaring_member_id: @member ref, + int unbound_id: @accessor ref); + +case @accessor.kind of + 1 = @getter +| 2 = @setter + ; + +init_only_accessors( + unique int id: @accessor ref); + +accessor_location( + int id: @accessor ref, + int loc: @location ref); + +events( + unique int id: @event, + string name: string ref, + int declaring_type_id: @type ref, + int type_id: @type_or_ref ref, + int unbound_id: @event ref); + +event_location( + int id: @event ref, + int loc: @location ref); + +event_accessors( + unique int id: @event_accessor, + int kind: int ref, + string name: string ref, + int declaring_event_id: @event ref, + int unbound_id: @event_accessor ref); + +case @event_accessor.kind of + 1 = @add_event_accessor +| 2 = @remove_event_accessor + ; + +event_accessor_location( + int id: @event_accessor ref, + int loc: @location ref); + +operators( + unique int id: @operator, + string name: string ref, + string symbol: string ref, + int declaring_type_id: @type ref, + int type_id: @type_or_ref ref, + int unbound_id: @operator ref); + +operator_location( + int id: @operator ref, + int loc: @location ref); + +constant_value( + int id: @variable ref, + string value: string ref); + +/** CALLABLES **/ + +@callable = @method | @constructor | @destructor | @operator | @callable_accessor | @anonymous_function_expr | @local_function; + +@callable_accessor = @accessor | @event_accessor; + +methods( + unique int id: @method, + string name: string ref, + int declaring_type_id: @type ref, + int type_id: @type_or_ref ref, + int unbound_id: @method ref); + +method_location( + int id: @method ref, + int loc: @location ref); + +constructors( + unique int id: @constructor, + string name: string ref, + int declaring_type_id: @type ref, + int unbound_id: @constructor ref); + +constructor_location( + int id: @constructor ref, + int loc: @location ref); + +destructors( + unique int id: @destructor, + string name: string ref, + int declaring_type_id: @type ref, + int unbound_id: @destructor ref); + +destructor_location( + int id: @destructor ref, + int loc: @location ref); + +overrides( + int id: @callable ref, + int base_id: @callable ref); + +explicitly_implements( + int id: @member ref, + int interface_id: @interface_or_ref ref); + +local_functions( + unique int id: @local_function, + string name: string ref, + int return_type: @type ref, + int unbound_id: @local_function ref); + +local_function_stmts( + unique int fn: @local_function_stmt ref, + int stmt: @local_function ref); + +/** VARIABLES **/ + +@variable = @local_scope_variable | @field; + +@local_scope_variable = @local_variable | @parameter; + +fields( + unique int id: @field, + int kind: int ref, + string name: string ref, + int declaring_type_id: @type ref, + int type_id: @type_or_ref ref, + int unbound_id: @field ref); + +case @field.kind of + 1 = @addressable_field +| 2 = @constant + ; + +field_location( + int id: @field ref, + int loc: @location ref); + +localvars( + unique int id: @local_variable, + int kind: int ref, + string name: string ref, + int implicitly_typed: int ref /* 0 = no, 1 = yes */, + int type_id: @type_or_ref ref, + int parent_id: @local_var_decl_expr ref); + +case @local_variable.kind of + 1 = @addressable_local_variable +| 2 = @local_constant +| 3 = @local_variable_ref + ; + +localvar_location( + unique int id: @local_variable ref, + int loc: @location ref); + +@parameterizable = @callable | @delegate_type | @indexer | @function_pointer_type | @extension_type; + +#keyset[name, parent_id] +#keyset[index, parent_id] +params( + unique int id: @parameter, + string name: string ref, + int type_id: @type_or_ref ref, + int index: int ref, + int mode: int ref, /* value = 0, ref = 1, out = 2, params/array = 3, this = 4, in = 5, ref readonly = 6 */ + int parent_id: @parameterizable ref, + int unbound_id: @parameter ref); + +param_location( + int id: @parameter ref, + int loc: @location ref); + +@has_scoped_annotation = @local_scope_variable + +scoped_annotation( + int id: @has_scoped_annotation ref, + int kind: int ref // scoped ref = 1, scoped value = 2 + ); + +/** STATEMENTS **/ + +@exprorstmt_parent = @control_flow_element | @top_level_exprorstmt_parent; + +statements( + unique int id: @stmt, + int kind: int ref); + +#keyset[index, parent] +stmt_parent( + unique int stmt: @stmt ref, + int index: int ref, + int parent: @control_flow_element ref); + +@top_level_stmt_parent = @callable; + +// [index, parent] is not a keyset because the same parent may be compiled multiple times +stmt_parent_top_level( + unique int stmt: @stmt ref, + int index: int ref, + int parent: @top_level_stmt_parent ref); + +case @stmt.kind of + 1 = @block_stmt +| 2 = @expr_stmt +| 3 = @if_stmt +| 4 = @switch_stmt +| 5 = @while_stmt +| 6 = @do_stmt +| 7 = @for_stmt +| 8 = @foreach_stmt +| 9 = @break_stmt +| 10 = @continue_stmt +| 11 = @goto_stmt +| 12 = @goto_case_stmt +| 13 = @goto_default_stmt +| 14 = @throw_stmt +| 15 = @return_stmt +| 16 = @yield_stmt +| 17 = @try_stmt +| 18 = @checked_stmt +| 19 = @unchecked_stmt +| 20 = @lock_stmt +| 21 = @using_block_stmt +| 22 = @var_decl_stmt +| 23 = @const_decl_stmt +| 24 = @empty_stmt +| 25 = @unsafe_stmt +| 26 = @fixed_stmt +| 27 = @label_stmt +| 28 = @catch +| 29 = @case_stmt +| 30 = @local_function_stmt +| 31 = @using_decl_stmt + ; + +@using_stmt = @using_block_stmt | @using_decl_stmt; + +@labeled_stmt = @label_stmt | @case; + +@decl_stmt = @var_decl_stmt | @const_decl_stmt | @using_decl_stmt; + +@cond_stmt = @if_stmt | @switch_stmt; + +@loop_stmt = @while_stmt | @do_stmt | @for_stmt | @foreach_stmt; + +@jump_stmt = @break_stmt | @goto_any_stmt | @continue_stmt | @throw_stmt | @return_stmt + | @yield_stmt; + +@goto_any_stmt = @goto_default_stmt | @goto_case_stmt | @goto_stmt; + + +stmt_location( + unique int id: @stmt ref, + int loc: @location ref); + +catch_type( + unique int catch_id: @catch ref, + int type_id: @type_or_ref ref, + int kind: int ref /* explicit = 1, implicit = 2 */); + +foreach_stmt_info( + unique int id: @foreach_stmt ref, + int kind: int ref /* non-async = 1, async = 2 */); + +@foreach_symbol = @method | @property | @type_or_ref; + +#keyset[id, kind] +foreach_stmt_desugar( + int id: @foreach_stmt ref, + int symbol: @foreach_symbol ref, + int kind: int ref /* GetEnumeratorMethod = 1, CurrentProperty = 2, MoveNextMethod = 3, DisposeMethod = 4, ElementType = 5 */); + +/** EXPRESSIONS **/ + +expressions( + unique int id: @expr, + int kind: int ref, + int type_id: @type_or_ref ref); + +#keyset[index, parent] +expr_parent( + unique int expr: @expr ref, + int index: int ref, + int parent: @control_flow_element ref); + +@top_level_expr_parent = @attribute | @field | @property | @indexer | @parameter | @directive_if | @directive_elif; + +@top_level_exprorstmt_parent = @top_level_expr_parent | @top_level_stmt_parent; + +// [index, parent] is not a keyset because the same parent may be compiled multiple times +expr_parent_top_level( + unique int expr: @expr ref, + int index: int ref, + int parent: @top_level_exprorstmt_parent ref); + +case @expr.kind of +/* literal */ + 1 = @bool_literal_expr +| 2 = @char_literal_expr +| 3 = @decimal_literal_expr +| 4 = @int_literal_expr +| 5 = @long_literal_expr +| 6 = @uint_literal_expr +| 7 = @ulong_literal_expr +| 8 = @float_literal_expr +| 9 = @double_literal_expr +| 10 = @utf16_string_literal_expr +| 11 = @null_literal_expr +/* primary & unary */ +| 12 = @this_access_expr +| 13 = @base_access_expr +| 14 = @local_variable_access_expr +| 15 = @parameter_access_expr +| 16 = @field_access_expr +| 17 = @property_access_expr +| 18 = @method_access_expr +| 19 = @event_access_expr +| 20 = @indexer_access_expr +| 21 = @array_access_expr +| 22 = @type_access_expr +| 23 = @typeof_expr +| 24 = @method_invocation_expr +| 25 = @delegate_invocation_expr +| 26 = @operator_invocation_expr +| 27 = @cast_expr +| 28 = @object_creation_expr +| 29 = @explicit_delegate_creation_expr +| 30 = @implicit_delegate_creation_expr +| 31 = @array_creation_expr +| 32 = @default_expr +| 33 = @plus_expr +| 34 = @minus_expr +| 35 = @bit_not_expr +| 36 = @log_not_expr +| 37 = @post_incr_expr +| 38 = @post_decr_expr +| 39 = @pre_incr_expr +| 40 = @pre_decr_expr +/* multiplicative */ +| 41 = @mul_expr +| 42 = @div_expr +| 43 = @rem_expr +/* additive */ +| 44 = @add_expr +| 45 = @sub_expr +/* shift */ +| 46 = @lshift_expr +| 47 = @rshift_expr +/* relational */ +| 48 = @lt_expr +| 49 = @gt_expr +| 50 = @le_expr +| 51 = @ge_expr +/* equality */ +| 52 = @eq_expr +| 53 = @ne_expr +/* logical */ +| 54 = @bit_and_expr +| 55 = @bit_xor_expr +| 56 = @bit_or_expr +| 57 = @log_and_expr +| 58 = @log_or_expr +/* type testing */ +| 59 = @is_expr +| 60 = @as_expr +/* null coalescing */ +| 61 = @null_coalescing_expr +/* conditional */ +| 62 = @conditional_expr +/* assignment */ +| 63 = @simple_assign_expr +| 64 = @assign_add_expr +| 65 = @assign_sub_expr +| 66 = @assign_mul_expr +| 67 = @assign_div_expr +| 68 = @assign_rem_expr +| 69 = @assign_and_expr +| 70 = @assign_xor_expr +| 71 = @assign_or_expr +| 72 = @assign_lshift_expr +| 73 = @assign_rshift_expr +/* more */ +| 74 = @object_init_expr +| 75 = @collection_init_expr +| 76 = @array_init_expr +| 77 = @checked_expr +| 78 = @unchecked_expr +| 79 = @constructor_init_expr +| 80 = @add_event_expr +| 81 = @remove_event_expr +| 82 = @par_expr +| 83 = @local_var_decl_expr +| 84 = @lambda_expr +| 85 = @anonymous_method_expr +| 86 = @namespace_expr +/* dynamic */ +| 92 = @dynamic_element_access_expr +| 93 = @dynamic_member_access_expr +/* unsafe */ +| 100 = @pointer_indirection_expr +| 101 = @address_of_expr +| 102 = @sizeof_expr +/* async */ +| 103 = @await_expr +/* C# 6.0 */ +| 104 = @nameof_expr +| 105 = @interpolated_string_expr +| 106 = @unknown_expr +/* C# 7.0 */ +| 107 = @throw_expr +| 108 = @tuple_expr +| 109 = @local_function_invocation_expr +| 110 = @ref_expr +| 111 = @discard_expr +/* C# 8.0 */ +| 112 = @range_expr +| 113 = @index_expr +| 114 = @switch_expr +| 115 = @recursive_pattern_expr +| 116 = @property_pattern_expr +| 117 = @positional_pattern_expr +| 118 = @switch_case_expr +| 119 = @assign_coalesce_expr +| 120 = @suppress_nullable_warning_expr +| 121 = @namespace_access_expr +/* C# 9.0 */ +| 122 = @lt_pattern_expr +| 123 = @gt_pattern_expr +| 124 = @le_pattern_expr +| 125 = @ge_pattern_expr +| 126 = @not_pattern_expr +| 127 = @and_pattern_expr +| 128 = @or_pattern_expr +| 129 = @function_pointer_invocation_expr +| 130 = @with_expr +/* C# 11.0 */ +| 131 = @list_pattern_expr +| 132 = @slice_pattern_expr +| 133 = @urshift_expr +| 134 = @assign_urshift_expr +| 135 = @utf8_string_literal_expr +/* C# 12.0 */ +| 136 = @collection_expr +| 137 = @spread_element_expr +| 138 = @interpolated_string_insert_expr +/* Preprocessor */ +| 999 = @define_symbol_expr +; + +@switch = @switch_stmt | @switch_expr; +@case = @case_stmt | @switch_case_expr; +@pattern_match = @case | @is_expr; +@unary_pattern_expr = @not_pattern_expr; +@relational_pattern_expr = @gt_pattern_expr | @lt_pattern_expr | @ge_pattern_expr | @le_pattern_expr; +@binary_pattern_expr = @and_pattern_expr | @or_pattern_expr; + +@integer_literal_expr = @int_literal_expr | @long_literal_expr | @uint_literal_expr | @ulong_literal_expr; +@real_literal_expr = @float_literal_expr | @double_literal_expr | @decimal_literal_expr; +@string_literal_expr = @utf16_string_literal_expr | @utf8_string_literal_expr; +@literal_expr = @bool_literal_expr | @char_literal_expr | @integer_literal_expr | @real_literal_expr + | @string_literal_expr | @null_literal_expr; + +@assign_expr = @simple_assign_expr | @assign_op_expr | @local_var_decl_expr; +@assign_op_call_expr = @assign_arith_expr | @assign_bitwise_expr +@assign_op_expr = @assign_op_call_expr | @assign_event_expr | @assign_coalesce_expr; +@assign_event_expr = @add_event_expr | @remove_event_expr; + +@add_operation = @add_expr | @assign_add_expr; +@sub_operation = @sub_expr | @assign_sub_expr; +@mul_operation = @mul_expr | @assign_mul_expr; +@div_operation = @div_expr | @assign_div_expr; +@rem_operation = @rem_expr | @assign_rem_expr; +@and_operation = @bit_and_expr | @assign_and_expr; +@xor_operation = @bit_xor_expr | @assign_xor_expr; +@or_operation = @bit_or_expr | @assign_or_expr; +@lshift_operation = @lshift_expr | @assign_lshift_expr; +@rshift_operation = @rshift_expr | @assign_rshift_expr; +@urshift_operation = @urshift_expr | @assign_urshift_expr; +@null_coalescing_operation = @null_coalescing_expr | @assign_coalesce_expr; + +@assign_arith_expr = @assign_add_expr | @assign_sub_expr | @assign_mul_expr | @assign_div_expr + | @assign_rem_expr +@assign_bitwise_expr = @assign_and_expr | @assign_or_expr | @assign_xor_expr + | @assign_lshift_expr | @assign_rshift_expr | @assign_urshift_expr; + +@member_access_expr = @field_access_expr | @property_access_expr | @indexer_access_expr | @event_access_expr + | @method_access_expr | @type_access_expr | @dynamic_member_access_expr; +@access_expr = @member_access_expr | @this_access_expr | @base_access_expr | @assignable_access_expr | @namespace_access_expr; +@element_access_expr = @indexer_access_expr | @array_access_expr | @dynamic_element_access_expr; + +@local_variable_access = @local_variable_access_expr | @local_var_decl_expr; +@local_scope_variable_access_expr = @parameter_access_expr | @local_variable_access; +@variable_access_expr = @local_scope_variable_access_expr | @field_access_expr; + +@assignable_access_expr = @variable_access_expr | @property_access_expr | @element_access_expr + | @event_access_expr | @dynamic_member_access_expr; + +@objectorcollection_init_expr = @object_init_expr | @collection_init_expr; + +@delegate_creation_expr = @explicit_delegate_creation_expr | @implicit_delegate_creation_expr; + +@bin_arith_op_expr = @mul_expr | @div_expr | @rem_expr | @add_expr | @sub_expr; +@incr_op_expr = @pre_incr_expr | @post_incr_expr; +@decr_op_expr = @pre_decr_expr | @post_decr_expr; +@mut_op_expr = @incr_op_expr | @decr_op_expr; +@un_arith_op_expr = @plus_expr | @minus_expr | @mut_op_expr; +@arith_op_expr = @bin_arith_op_expr | @un_arith_op_expr; + +@ternary_log_op_expr = @conditional_expr; +@bin_log_op_expr = @log_and_expr | @log_or_expr | @null_coalescing_expr; +@un_log_op_expr = @log_not_expr; +@log_expr = @un_log_op_expr | @bin_log_op_expr | @ternary_log_op_expr; + +@bin_bit_op_expr = @bit_and_expr | @bit_or_expr | @bit_xor_expr | @lshift_expr + | @rshift_expr | @urshift_expr; +@un_bit_op_expr = @bit_not_expr; +@bit_expr = @un_bit_op_expr | @bin_bit_op_expr; + +@equality_op_expr = @eq_expr | @ne_expr; +@rel_op_expr = @gt_expr | @lt_expr| @ge_expr | @le_expr; +@comp_expr = @equality_op_expr | @rel_op_expr; + +@op_expr = @un_op | @bin_op | @ternary_op; + +@ternary_op = @ternary_log_op_expr; +@bin_op = @assign_expr | @bin_arith_op_expr | @bin_log_op_expr | @bin_bit_op_expr | @comp_expr; +@un_op = @un_arith_op_expr | @un_log_op_expr | @un_bit_op_expr | @sizeof_expr + | @pointer_indirection_expr | @address_of_expr; + +@anonymous_function_expr = @lambda_expr | @anonymous_method_expr; + +@op_invoke_expr = @operator_invocation_expr | @assign_op_call_expr +@call = @method_invocation_expr | @constructor_init_expr | @op_invoke_expr + | @delegate_invocation_expr | @object_creation_expr | @call_access_expr + | @local_function_invocation_expr | @function_pointer_invocation_expr; + +@call_access_expr = @property_access_expr | @event_access_expr | @indexer_access_expr; + +@late_bindable_expr = @dynamic_element_access_expr | @dynamic_member_access_expr + | @object_creation_expr | @method_invocation_expr | @op_invoke_expr; + +@throw_element = @throw_expr | @throw_stmt; + +@implicitly_typeable_object_creation_expr = @object_creation_expr | @explicit_delegate_creation_expr; + +implicitly_typed_array_creation( + unique int id: @array_creation_expr ref); + +explicitly_sized_array_creation( + unique int id: @array_creation_expr ref); + +stackalloc_array_creation( + unique int id: @array_creation_expr ref); + +implicitly_typed_object_creation( + unique int id: @implicitly_typeable_object_creation_expr ref); + +mutator_invocation_mode( + unique int id: @operator_invocation_expr ref, + int mode: int ref /* prefix = 1, postfix = 2*/); + +expr_value( + unique int id: @expr ref, + string value: string ref); + +expr_call( + unique int caller_id: @expr ref, + int target_id: @callable ref); + +expr_access( + unique int accesser_id: @access_expr ref, + int target_id: @accessible ref); + +@accessible = @method | @assignable | @local_function | @namespace; + +expr_location( + unique int id: @expr ref, + int loc: @location ref); + +dynamic_member_name( + unique int id: @late_bindable_expr ref, + string name: string ref); + +@qualifiable_expr = @member_access_expr + | @method_invocation_expr + | @element_access_expr + | @assign_op_call_expr; + +conditional_access( + unique int id: @qualifiable_expr ref); + +expr_argument( + unique int id: @expr ref, + int mode: int ref); + /* mode is the same as params: value = 0, ref = 1, out = 2 */ + +expr_argument_name( + unique int id: @expr ref, + string name: string ref); + +lambda_expr_return_type( + unique int id: @lambda_expr ref, + int type_id: @type_or_ref ref); + +/* Compiler generated */ + +compiler_generated(unique int id: @element ref); + +/** CONTROL/DATA FLOW **/ + +@control_flow_element = @stmt | @expr | @parameter; + +/* XML Files */ + +xmlEncoding ( + unique int id: @file ref, + string encoding: string ref); + +xmlDTDs( + unique int id: @xmldtd, + string root: string ref, + string publicId: string ref, + string systemId: string ref, + int fileid: @file ref); + +xmlElements( + unique int id: @xmlelement, + string name: string ref, + int parentid: @xmlparent ref, + int idx: int ref, + int fileid: @file ref); + +xmlAttrs( + unique int id: @xmlattribute, + int elementid: @xmlelement ref, + string name: string ref, + string value: string ref, + int idx: int ref, + int fileid: @file ref); + +xmlNs( + int id: @xmlnamespace, + string prefixName: string ref, + string URI: string ref, + int fileid: @file ref); + +xmlHasNs( + int elementId: @xmlnamespaceable ref, + int nsId: @xmlnamespace ref, + int fileid: @file ref); + +xmlComments( + unique int id: @xmlcomment, + string text: string ref, + int parentid: @xmlparent ref, + int fileid: @file ref); + +xmlChars( + unique int id: @xmlcharacters, + string text: string ref, + int parentid: @xmlparent ref, + int idx: int ref, + int isCDATA: int ref, + int fileid: @file ref); + +@xmlparent = @file | @xmlelement; +@xmlnamespaceable = @xmlelement | @xmlattribute; + +xmllocations( + int xmlElement: @xmllocatable ref, + int location: @location_default ref); + +@xmllocatable = @xmlcharacters | @xmlelement | @xmlcomment | @xmlattribute | @xmldtd | @file | @xmlnamespace; + +/* Comments */ + +commentline( + unique int id: @commentline, + int kind: int ref, + string text: string ref, + string rawtext: string ref); + +case @commentline.kind of + 0 = @singlelinecomment +| 1 = @xmldoccomment +| 2 = @multilinecomment; + +commentline_location( + unique int id: @commentline ref, + int loc: @location ref); + +commentblock( + unique int id : @commentblock); + +commentblock_location( + unique int id: @commentblock ref, + int loc: @location ref); + +commentblock_binding( + int id: @commentblock ref, + int entity: @element ref, + int bindtype: int ref); /* 0: Parent, 1: Best, 2: Before, 3: After */ + +commentblock_child( + int id: @commentblock ref, + int commentline: @commentline ref, + int index: int ref); + +/* ASP.NET */ + +case @asp_element.kind of + 0=@asp_close_tag +| 1=@asp_code +| 2=@asp_comment +| 3=@asp_data_binding +| 4=@asp_directive +| 5=@asp_open_tag +| 6=@asp_quoted_string +| 7=@asp_text +| 8=@asp_xml_directive; + +@asp_attribute = @asp_code | @asp_data_binding | @asp_quoted_string; + +asp_elements( + unique int id: @asp_element, + int kind: int ref, + int loc: @location ref); + +asp_comment_server(unique int comment: @asp_comment ref); +asp_code_inline(unique int code: @asp_code ref); +asp_directive_attribute( + int directive: @asp_directive ref, + int index: int ref, + string name: string ref, + int value: @asp_quoted_string ref); +asp_directive_name( + unique int directive: @asp_directive ref, + string name: string ref); +asp_element_body( + unique int element: @asp_element ref, + string body: string ref); +asp_tag_attribute( + int tag: @asp_open_tag ref, + int index: int ref, + string name: string ref, + int attribute: @asp_attribute ref); +asp_tag_name( + unique int tag: @asp_open_tag ref, + string name: string ref); +asp_tag_isempty(int tag: @asp_open_tag ref); diff --git a/csharp/ql/lib/upgrades/3cabc77473cbbda95edebafea345c2e3fdfa12d9/semmlecode.csharp.dbscheme b/csharp/ql/lib/upgrades/3cabc77473cbbda95edebafea345c2e3fdfa12d9/semmlecode.csharp.dbscheme new file mode 100644 index 00000000000..d13c4c187d7 --- /dev/null +++ b/csharp/ql/lib/upgrades/3cabc77473cbbda95edebafea345c2e3fdfa12d9/semmlecode.csharp.dbscheme @@ -0,0 +1,1511 @@ +/* This is a dummy line to alter the dbscheme, so we can make a database upgrade + * without actually changing any of the dbscheme predicates. It contains a date + * to allow for such updates in the future as well. + * + * 2021-07-14 + * + * DO NOT remove this comment carelessly, since it can revert the dbscheme back to a + * previously seen state (matching a previously seen SHA), which would make the upgrade + * mechanism not work properly. + */ + +/** + * An invocation of the compiler. Note that more than one file may be + * compiled per invocation. For example, this command compiles three + * source files: + * + * csc f1.cs f2.cs f3.cs + * + * The `id` simply identifies the invocation, while `cwd` is the working + * directory from which the compiler was invoked. + */ +compilations( + unique int id : @compilation, + string cwd : string ref +); + +compilation_info( + int id : @compilation ref, + string info_key: string ref, + string info_value: string ref +) + +/** + * The arguments that were passed to the extractor for a compiler + * invocation. If `id` is for the compiler invocation + * + * csc f1.cs f2.cs f3.cs + * + * then typically there will be rows for + * + * num | arg + * --- | --- + * 0 | --compiler + * 1 | *path to compiler* + * 2 | f1.cs + * 3 | f2.cs + * 4 | f3.cs + */ +#keyset[id, num] +compilation_args( + int id : @compilation ref, + int num : int ref, + string arg : string ref +); + +/** + * The expanded arguments that were passed to the extractor for a + * compiler invocation. This is similar to `compilation_args`, but + * for a `@someFile.rsp` argument, it includes the arguments from that + * file, rather than just taking the argument literally. + */ +#keyset[id, num] +compilation_expanded_args( + int id : @compilation ref, + int num : int ref, + string arg : string ref +); + +/** + * The source files that are compiled by a compiler invocation. + * If `id` is for the compiler invocation + * + * csc f1.cs f2.cs f3.cs + * + * then there will be rows for + * + * num | arg + * --- | --- + * 0 | f1.cs + * 1 | f2.cs + * 2 | f3.cs + */ +#keyset[id, num] +compilation_compiling_files( + int id : @compilation ref, + int num : int ref, + int file : @file ref +); + +/** + * The references used by a compiler invocation. + * If `id` is for the compiler invocation + * + * csc f1.cs f2.cs f3.cs /r:ref1.dll /r:ref2.dll /r:ref3.dll + * + * then there will be rows for + * + * num | arg + * --- | --- + * 0 | ref1.dll + * 1 | ref2.dll + * 2 | ref3.dll + */ +#keyset[id, num] +compilation_referencing_files( + int id : @compilation ref, + int num : int ref, + int file : @file ref +); + +/** + * The time taken by the extractor for a compiler invocation. + * + * For each file `num`, there will be rows for + * + * kind | seconds + * ---- | --- + * 1 | CPU seconds used by the extractor frontend + * 2 | Elapsed seconds during the extractor frontend + * 3 | CPU seconds used by the extractor backend + * 4 | Elapsed seconds during the extractor backend + */ +#keyset[id, num, kind] +compilation_time( + int id : @compilation ref, + int num : int ref, + /* kind: + 1 = frontend_cpu_seconds + 2 = frontend_elapsed_seconds + 3 = extractor_cpu_seconds + 4 = extractor_elapsed_seconds + */ + int kind : int ref, + float seconds : float ref +); + +/** + * An error or warning generated by the extractor. + * The diagnostic message `diagnostic` was generated during compiler + * invocation `compilation`, and is the `file_number_diagnostic_number`th + * message generated while extracting the `file_number`th file of that + * invocation. + */ +#keyset[compilation, file_number, file_number_diagnostic_number] +diagnostic_for( + unique int diagnostic : @diagnostic ref, + int compilation : @compilation ref, + int file_number : int ref, + int file_number_diagnostic_number : int ref +); + +diagnostics( + unique int id: @diagnostic, + int severity: int ref, + string error_tag: string ref, + string error_message: string ref, + string full_error_message: string ref, + int location: @location ref +); + +extractor_messages( + unique int id: @extractor_message, + int severity: int ref, + string origin : string ref, + string text : string ref, + string entity : string ref, + int location: @location ref, + string stack_trace : string ref +); + +/** + * If extraction was successful, then `cpu_seconds` and + * `elapsed_seconds` are the CPU time and elapsed time (respectively) + * that extraction took for compiler invocation `id`. + */ +compilation_finished( + unique int id : @compilation ref, + float cpu_seconds : float ref, + float elapsed_seconds : float ref +); + +compilation_assembly( + unique int id : @compilation ref, + int assembly: @assembly ref +) + +// Populated by the CSV extractor +externalData( + int id: @externalDataElement, + string path: string ref, + int column: int ref, + string value: string ref); + +sourceLocationPrefix( + string prefix: string ref); + +/* + * Overlay support + */ + +/** + * The CLI will automatically emit the tuple `databaseMetadata("isOverlay", "true")`, + * along with an `overlayChangedFiles` tuple for each new/modified/deleted file, + * when building an overlay database, and these can be used by the discard predicates. + */ +databaseMetadata( + string metadataKey : string ref, + string value : string ref +); + +overlayChangedFiles( + string path : string ref +); + +/* + * C# dbscheme + */ + +/** ELEMENTS **/ + +@element = @declaration | @stmt | @expr | @modifier | @attribute | @namespace_declaration + | @using_directive | @type_parameter_constraints | @externalDataElement + | @xmllocatable | @asp_element | @namespace | @preprocessor_directive; + +@declaration = @callable | @generic | @assignable | @namespace; + +@named_element = @namespace | @declaration; + +@declaration_with_accessors = @property | @indexer | @event; + +@assignable = @variable | @assignable_with_accessors | @event; + +@assignable_with_accessors = @property | @indexer; + +@attributable = @assembly | @field | @parameter | @operator | @method | @constructor + | @destructor | @callable_accessor | @value_or_ref_type | @declaration_with_accessors + | @local_function | @lambda_expr; + +/** LOCATIONS, ASEMMBLIES, MODULES, FILES and FOLDERS **/ + +@location = @location_default | @assembly; + +@locatable = @declaration_with_accessors | @callable_accessor | @declaration_or_directive + | @diagnostic | @extractor_message | @preprocessor_directive | @attribute | @type_mention | @type_parameter_constraints + | @declaration_with_accessors | @callable_accessor | @operator | @method + | @constructor | @destructor | @field | @local_variable | @parameter | @stmt | @expr + | @xmllocatable | @commentline | @commentblock | @asp_element + +locations_default( + unique int id: @location_default, + int file: @file ref, + int beginLine: int ref, + int beginColumn: int ref, + int endLine: int ref, + int endColumn: int ref); + +locations_mapped( + unique int id: @location_default ref, + int mapped_to: @location_default ref); + +@sourceline = @file | @callable | @xmllocatable; + +numlines( + int element_id: @sourceline ref, + int num_lines: int ref, + int num_code: int ref, + int num_comment: int ref); + +assemblies( + unique int id: @assembly, + int file: @file ref, + string fullname: string ref, + string name: string ref, + string version: string ref); + +files( + unique int id: @file, + string name: string ref); + +folders( + unique int id: @folder, + string name: string ref); + +@container = @folder | @file ; + +containerparent( + int parent: @container ref, + unique int child: @container ref); + +file_extraction_mode( + unique int file: @file ref, + int mode: int ref + /* 0 = normal, 1 = standalone extractor */ + ); + +/** NAMESPACES **/ + +@type_container = @namespace | @type; + +namespaces( + unique int id: @namespace, + string name: string ref); + +namespace_declarations( + unique int id: @namespace_declaration, + int namespace_id: @namespace ref); + +namespace_declaration_location( + unique int id: @namespace_declaration ref, + int loc: @location ref); + +parent_namespace( + unique int child_id: @type_container ref, + int namespace_id: @namespace ref); + +@declaration_or_directive = @namespace_declaration | @type | @using_directive; + +parent_namespace_declaration( + int child_id: @declaration_or_directive ref, // cannot be unique because of partial classes + int namespace_id: @namespace_declaration ref); + +@using_directive = @using_namespace_directive | @using_static_directive; + +using_global( + unique int id: @using_directive ref +); + +using_namespace_directives( + unique int id: @using_namespace_directive, + int namespace_id: @namespace ref); + +using_static_directives( + unique int id: @using_static_directive, + int type_id: @type_or_ref ref); + +using_directive_location( + unique int id: @using_directive ref, + int loc: @location ref); + +@preprocessor_directive = @pragma_warning | @pragma_checksum | @directive_define | @directive_undefine | @directive_warning + | @directive_error | @directive_nullable | @directive_line | @directive_region | @directive_endregion | @directive_if + | @directive_elif | @directive_else | @directive_endif; + +@conditional_directive = @directive_if | @directive_elif; +@branch_directive = @directive_if | @directive_elif | @directive_else; + +directive_ifs( + unique int id: @directive_if, + int branchTaken: int ref, /* 0: false, 1: true */ + int conditionValue: int ref); /* 0: false, 1: true */ + +directive_elifs( + unique int id: @directive_elif, + int branchTaken: int ref, /* 0: false, 1: true */ + int conditionValue: int ref, /* 0: false, 1: true */ + int parent: @directive_if ref, + int index: int ref); + +directive_elses( + unique int id: @directive_else, + int branchTaken: int ref, /* 0: false, 1: true */ + int parent: @directive_if ref, + int index: int ref); + +#keyset[id, start] +directive_endifs( + unique int id: @directive_endif, + unique int start: @directive_if ref); + +directive_define_symbols( + unique int id: @define_symbol_expr ref, + string name: string ref); + +directive_regions( + unique int id: @directive_region, + string name: string ref); + +#keyset[id, start] +directive_endregions( + unique int id: @directive_endregion, + unique int start: @directive_region ref); + +directive_lines( + unique int id: @directive_line, + int kind: int ref); /* 0: default, 1: hidden, 2: numeric, 3: span */ + +directive_line_value( + unique int id: @directive_line ref, + int line: int ref); + +directive_line_file( + unique int id: @directive_line ref, + int file: @file ref); + +directive_line_offset( + unique int id: @directive_line ref, + int offset: int ref); + +directive_line_span( + unique int id: @directive_line ref, + int startLine: int ref, + int startColumn: int ref, + int endLine: int ref, + int endColumn: int ref); + +directive_nullables( + unique int id: @directive_nullable, + int setting: int ref, /* 0: disable, 1: enable, 2: restore */ + int target: int ref); /* 0: none, 1: annotations, 2: warnings */ + +directive_warnings( + unique int id: @directive_warning, + string message: string ref); + +directive_errors( + unique int id: @directive_error, + string message: string ref); + +directive_undefines( + unique int id: @directive_undefine, + string name: string ref); + +directive_defines( + unique int id: @directive_define, + string name: string ref); + +pragma_checksums( + unique int id: @pragma_checksum, + int file: @file ref, + string guid: string ref, + string bytes: string ref); + +pragma_warnings( + unique int id: @pragma_warning, + int kind: int ref /* 0 = disable, 1 = restore */); + +#keyset[id, index] +pragma_warning_error_codes( + int id: @pragma_warning ref, + string errorCode: string ref, + int index: int ref); + +preprocessor_directive_location( + unique int id: @preprocessor_directive ref, + int loc: @location ref); + +preprocessor_directive_compilation( + int id: @preprocessor_directive ref, + int compilation: @compilation ref); + +preprocessor_directive_active( + unique int id: @preprocessor_directive ref, + int active: int ref); /* 0: false, 1: true */ + +/** TYPES **/ + +types( + unique int id: @type, + int kind: int ref, + string name: string ref); + +case @type.kind of + 1 = @bool_type +| 2 = @char_type +| 3 = @decimal_type +| 4 = @sbyte_type +| 5 = @short_type +| 6 = @int_type +| 7 = @long_type +| 8 = @byte_type +| 9 = @ushort_type +| 10 = @uint_type +| 11 = @ulong_type +| 12 = @float_type +| 13 = @double_type +| 14 = @enum_type +| 15 = @struct_type +| 17 = @class_type +| 19 = @interface_type +| 20 = @delegate_type +| 21 = @null_type +| 22 = @type_parameter +| 23 = @pointer_type +| 24 = @nullable_type +| 25 = @array_type +| 26 = @void_type +| 27 = @int_ptr_type +| 28 = @uint_ptr_type +| 29 = @dynamic_type +| 30 = @arglist_type +| 31 = @unknown_type +| 32 = @tuple_type +| 33 = @function_pointer_type +| 34 = @inline_array_type +| 35 = @extension_type + ; + +@simple_type = @bool_type | @char_type | @integral_type | @floating_point_type | @decimal_type; +@integral_type = @signed_integral_type | @unsigned_integral_type; +@signed_integral_type = @sbyte_type | @short_type | @int_type | @long_type; +@unsigned_integral_type = @byte_type | @ushort_type | @uint_type | @ulong_type; +@floating_point_type = @float_type | @double_type; +@value_type = @simple_type | @enum_type | @struct_type | @nullable_type | @int_ptr_type + | @uint_ptr_type | @tuple_type | @void_type | @inline_array_type; +@ref_type = @class_type | @interface_type | @array_type | @delegate_type | @null_type + | @dynamic_type | @extension_type; +@value_or_ref_type = @value_type | @ref_type; + +typerefs( + unique int id: @typeref, + string name: string ref); + +typeref_type( + int id: @typeref ref, + unique int typeId: @type ref); + +@type_or_ref = @type | @typeref; + +array_element_type( + unique int array: @array_type ref, + int dimension: int ref, + int rank: int ref, + int element: @type_or_ref ref); + +nullable_underlying_type( + unique int nullable: @nullable_type ref, + int underlying: @type_or_ref ref); + +pointer_referent_type( + unique int pointer: @pointer_type ref, + int referent: @type_or_ref ref); + +enum_underlying_type( + unique int enum_id: @enum_type ref, + int underlying_type_id: @type_or_ref ref); + +delegate_return_type( + unique int delegate_id: @delegate_type ref, + int return_type_id: @type_or_ref ref); + +function_pointer_return_type( + unique int function_pointer_id: @function_pointer_type ref, + int return_type_id: @type_or_ref ref); + +extension_receiver_type( + unique int extension: @extension_type ref, + int receiver_type_id: @type_or_ref ref); + +extend( + int sub: @type ref, + int super: @type_or_ref ref); + +anonymous_types( + unique int id: @type ref); + +@interface_or_ref = @interface_type | @typeref; + +implement( + int sub: @type ref, + int super: @type_or_ref ref); + +type_location( + int id: @type ref, + int loc: @location ref); + +tuple_underlying_type( + unique int tuple: @tuple_type ref, + int struct: @type_or_ref ref); + +#keyset[tuple, index] +tuple_element( + int tuple: @tuple_type ref, + int index: int ref, + unique int field: @field ref); + +attributes( + unique int id: @attribute, + int kind: int ref, + int type_id: @type_or_ref ref, + int target: @attributable ref); + +case @attribute.kind of + 0 = @attribute_default +| 1 = @attribute_return +| 2 = @attribute_assembly +| 3 = @attribute_module +; + +attribute_location( + int id: @attribute ref, + int loc: @location ref); + +@type_mention_parent = @element | @type_mention; + +type_mention( + unique int id: @type_mention, + int type_id: @type_or_ref ref, + int parent: @type_mention_parent ref); + +type_mention_location( + unique int id: @type_mention ref, + int loc: @location ref); + +@has_type_annotation = @assignable | @type_parameter | @callable | @expr | @delegate_type | @generic | @function_pointer_type; + +/** + * A direct annotation on an entity, for example `string? x;`. + * + * Annotations: + * 2 = reftype is not annotated "!" + * 3 = reftype is annotated "?" + * 4 = readonly ref type / in parameter + * 5 = ref type parameter, return or local variable + * 6 = out parameter + * + * Note that the annotation depends on the element it annotates. + * @assignable: The annotation is on the type of the assignable, for example the variable type. + * @type_parameter: The annotation is on the reftype constraint + * @callable: The annotation is on the return type + * @array_type: The annotation is on the element type + */ +type_annotation(int id: @has_type_annotation ref, int annotation: int ref); + +nullability(unique int nullability: @nullability, int kind: int ref); + +case @nullability.kind of + 0 = @oblivious +| 1 = @not_annotated +| 2 = @annotated +; + +#keyset[parent, index] +nullability_parent(int nullability: @nullability ref, int index: int ref, int parent: @nullability ref) + +type_nullability(int id: @has_type_annotation ref, int nullability: @nullability ref); + +/** + * The nullable flow state of an expression, as determined by Roslyn. + * 0 = none (default, not populated) + * 1 = not null + * 2 = maybe null + */ +expr_flowstate(unique int id: @expr ref, int state: int ref); + +/** GENERICS **/ + +@generic = @type | @method | @local_function; + +type_parameters( + unique int id: @type_parameter ref, + int index: int ref, + int generic_id: @generic ref, + int variance: int ref /* none = 0, out = 1, in = 2 */); + +#keyset[constructed_id, index] +type_arguments( + int id: @type_or_ref ref, + int index: int ref, + int constructed_id: @generic_or_ref ref); + +@generic_or_ref = @generic | @typeref; + +constructed_generic( + unique int constructed: @generic ref, + int generic: @generic_or_ref ref); + +type_parameter_constraints( + unique int id: @type_parameter_constraints, + int param_id: @type_parameter ref); + +type_parameter_constraints_location( + int id: @type_parameter_constraints ref, + int loc: @location ref); + +general_type_parameter_constraints( + int id: @type_parameter_constraints ref, + int kind: int ref /* class = 1, struct = 2, new = 3 */); + +specific_type_parameter_constraints( + int id: @type_parameter_constraints ref, + int base_id: @type_or_ref ref); + +specific_type_parameter_nullability( + int id: @type_parameter_constraints ref, + int base_id: @type_or_ref ref, + int nullability: @nullability ref); + +/** FUNCTION POINTERS */ + +function_pointer_calling_conventions( + int id: @function_pointer_type ref, + int kind: int ref); + +#keyset[id, index] +has_unmanaged_calling_conventions( + int id: @function_pointer_type ref, + int index: int ref, + int conv_id: @type_or_ref ref); + +/** MODIFIERS */ + +@modifiable = @modifiable_direct | @event_accessor; + +@modifiable_direct = @member | @accessor | @local_function | @anonymous_function_expr; + +modifiers( + unique int id: @modifier, + string name: string ref); + +has_modifiers( + int id: @modifiable_direct ref, + int mod_id: @modifier ref); + +/** MEMBERS **/ + +@member = @method | @constructor | @destructor | @field | @property | @event | @operator | @indexer | @type; + +@named_exprorstmt = @goto_stmt | @labeled_stmt | @expr; + +@virtualizable = @method | @property | @indexer | @event | @operator; + +exprorstmt_name( + unique int parent_id: @named_exprorstmt ref, + string name: string ref); + +nested_types( + unique int id: @type ref, + int declaring_type_id: @type ref, + int unbound_id: @type ref); + +properties( + unique int id: @property, + string name: string ref, + int declaring_type_id: @type ref, + int type_id: @type_or_ref ref, + int unbound_id: @property ref); + +property_location( + int id: @property ref, + int loc: @location ref); + +indexers( + unique int id: @indexer, + string name: string ref, + int declaring_type_id: @type ref, + int type_id: @type_or_ref ref, + int unbound_id: @indexer ref); + +indexer_location( + int id: @indexer ref, + int loc: @location ref); + +accessors( + unique int id: @accessor, + int kind: int ref, + string name: string ref, + int declaring_member_id: @member ref, + int unbound_id: @accessor ref); + +case @accessor.kind of + 1 = @getter +| 2 = @setter + ; + +init_only_accessors( + unique int id: @accessor ref); + +accessor_location( + int id: @accessor ref, + int loc: @location ref); + +events( + unique int id: @event, + string name: string ref, + int declaring_type_id: @type ref, + int type_id: @type_or_ref ref, + int unbound_id: @event ref); + +event_location( + int id: @event ref, + int loc: @location ref); + +event_accessors( + unique int id: @event_accessor, + int kind: int ref, + string name: string ref, + int declaring_event_id: @event ref, + int unbound_id: @event_accessor ref); + +case @event_accessor.kind of + 1 = @add_event_accessor +| 2 = @remove_event_accessor + ; + +event_accessor_location( + int id: @event_accessor ref, + int loc: @location ref); + +operators( + unique int id: @operator, + string name: string ref, + string symbol: string ref, + int declaring_type_id: @type ref, + int type_id: @type_or_ref ref, + int unbound_id: @operator ref); + +operator_location( + int id: @operator ref, + int loc: @location ref); + +constant_value( + int id: @variable ref, + string value: string ref); + +/** CALLABLES **/ + +@callable = @method | @constructor | @destructor | @operator | @callable_accessor | @anonymous_function_expr | @local_function; + +@callable_accessor = @accessor | @event_accessor; + +methods( + unique int id: @method, + string name: string ref, + int declaring_type_id: @type ref, + int type_id: @type_or_ref ref, + int unbound_id: @method ref); + +method_location( + int id: @method ref, + int loc: @location ref); + +constructors( + unique int id: @constructor, + string name: string ref, + int declaring_type_id: @type ref, + int unbound_id: @constructor ref); + +constructor_location( + int id: @constructor ref, + int loc: @location ref); + +destructors( + unique int id: @destructor, + string name: string ref, + int declaring_type_id: @type ref, + int unbound_id: @destructor ref); + +destructor_location( + int id: @destructor ref, + int loc: @location ref); + +overrides( + int id: @callable ref, + int base_id: @callable ref); + +explicitly_implements( + int id: @member ref, + int interface_id: @interface_or_ref ref); + +local_functions( + unique int id: @local_function, + string name: string ref, + int return_type: @type ref, + int unbound_id: @local_function ref); + +local_function_stmts( + unique int fn: @local_function_stmt ref, + int stmt: @local_function ref); + +/** VARIABLES **/ + +@variable = @local_scope_variable | @field; + +@local_scope_variable = @local_variable | @parameter; + +fields( + unique int id: @field, + int kind: int ref, + string name: string ref, + int declaring_type_id: @type ref, + int type_id: @type_or_ref ref, + int unbound_id: @field ref); + +case @field.kind of + 1 = @addressable_field +| 2 = @constant + ; + +field_location( + int id: @field ref, + int loc: @location ref); + +localvars( + unique int id: @local_variable, + int kind: int ref, + string name: string ref, + int implicitly_typed: int ref /* 0 = no, 1 = yes */, + int type_id: @type_or_ref ref, + int parent_id: @local_var_decl_expr ref); + +case @local_variable.kind of + 1 = @addressable_local_variable +| 2 = @local_constant +| 3 = @local_variable_ref + ; + +localvar_location( + unique int id: @local_variable ref, + int loc: @location ref); + +@parameterizable = @callable | @delegate_type | @indexer | @function_pointer_type | @extension_type; + +#keyset[name, parent_id] +#keyset[index, parent_id] +params( + unique int id: @parameter, + string name: string ref, + int type_id: @type_or_ref ref, + int index: int ref, + int mode: int ref, /* value = 0, ref = 1, out = 2, params/array = 3, this = 4, in = 5, ref readonly = 6 */ + int parent_id: @parameterizable ref, + int unbound_id: @parameter ref); + +param_location( + int id: @parameter ref, + int loc: @location ref); + +@has_scoped_annotation = @local_scope_variable + +scoped_annotation( + int id: @has_scoped_annotation ref, + int kind: int ref // scoped ref = 1, scoped value = 2 + ); + +/** STATEMENTS **/ + +@exprorstmt_parent = @control_flow_element | @top_level_exprorstmt_parent; + +statements( + unique int id: @stmt, + int kind: int ref); + +#keyset[index, parent] +stmt_parent( + unique int stmt: @stmt ref, + int index: int ref, + int parent: @control_flow_element ref); + +@top_level_stmt_parent = @callable; + +// [index, parent] is not a keyset because the same parent may be compiled multiple times +stmt_parent_top_level( + unique int stmt: @stmt ref, + int index: int ref, + int parent: @top_level_stmt_parent ref); + +case @stmt.kind of + 1 = @block_stmt +| 2 = @expr_stmt +| 3 = @if_stmt +| 4 = @switch_stmt +| 5 = @while_stmt +| 6 = @do_stmt +| 7 = @for_stmt +| 8 = @foreach_stmt +| 9 = @break_stmt +| 10 = @continue_stmt +| 11 = @goto_stmt +| 12 = @goto_case_stmt +| 13 = @goto_default_stmt +| 14 = @throw_stmt +| 15 = @return_stmt +| 16 = @yield_stmt +| 17 = @try_stmt +| 18 = @checked_stmt +| 19 = @unchecked_stmt +| 20 = @lock_stmt +| 21 = @using_block_stmt +| 22 = @var_decl_stmt +| 23 = @const_decl_stmt +| 24 = @empty_stmt +| 25 = @unsafe_stmt +| 26 = @fixed_stmt +| 27 = @label_stmt +| 28 = @catch +| 29 = @case_stmt +| 30 = @local_function_stmt +| 31 = @using_decl_stmt + ; + +@using_stmt = @using_block_stmt | @using_decl_stmt; + +@labeled_stmt = @label_stmt | @case; + +@decl_stmt = @var_decl_stmt | @const_decl_stmt | @using_decl_stmt; + +@cond_stmt = @if_stmt | @switch_stmt; + +@loop_stmt = @while_stmt | @do_stmt | @for_stmt | @foreach_stmt; + +@jump_stmt = @break_stmt | @goto_any_stmt | @continue_stmt | @throw_stmt | @return_stmt + | @yield_stmt; + +@goto_any_stmt = @goto_default_stmt | @goto_case_stmt | @goto_stmt; + + +stmt_location( + unique int id: @stmt ref, + int loc: @location ref); + +catch_type( + unique int catch_id: @catch ref, + int type_id: @type_or_ref ref, + int kind: int ref /* explicit = 1, implicit = 2 */); + +foreach_stmt_info( + unique int id: @foreach_stmt ref, + int kind: int ref /* non-async = 1, async = 2 */); + +@foreach_symbol = @method | @property | @type_or_ref; + +#keyset[id, kind] +foreach_stmt_desugar( + int id: @foreach_stmt ref, + int symbol: @foreach_symbol ref, + int kind: int ref /* GetEnumeratorMethod = 1, CurrentProperty = 2, MoveNextMethod = 3, DisposeMethod = 4, ElementType = 5 */); + +/** EXPRESSIONS **/ + +expressions( + unique int id: @expr, + int kind: int ref, + int type_id: @type_or_ref ref); + +#keyset[index, parent] +expr_parent( + unique int expr: @expr ref, + int index: int ref, + int parent: @control_flow_element ref); + +@top_level_expr_parent = @attribute | @field | @property | @indexer | @parameter | @directive_if | @directive_elif; + +@top_level_exprorstmt_parent = @top_level_expr_parent | @top_level_stmt_parent; + +// [index, parent] is not a keyset because the same parent may be compiled multiple times +expr_parent_top_level( + unique int expr: @expr ref, + int index: int ref, + int parent: @top_level_exprorstmt_parent ref); + +case @expr.kind of +/* literal */ + 1 = @bool_literal_expr +| 2 = @char_literal_expr +| 3 = @decimal_literal_expr +| 4 = @int_literal_expr +| 5 = @long_literal_expr +| 6 = @uint_literal_expr +| 7 = @ulong_literal_expr +| 8 = @float_literal_expr +| 9 = @double_literal_expr +| 10 = @utf16_string_literal_expr +| 11 = @null_literal_expr +/* primary & unary */ +| 12 = @this_access_expr +| 13 = @base_access_expr +| 14 = @local_variable_access_expr +| 15 = @parameter_access_expr +| 16 = @field_access_expr +| 17 = @property_access_expr +| 18 = @method_access_expr +| 19 = @event_access_expr +| 20 = @indexer_access_expr +| 21 = @array_access_expr +| 22 = @type_access_expr +| 23 = @typeof_expr +| 24 = @method_invocation_expr +| 25 = @delegate_invocation_expr +| 26 = @operator_invocation_expr +| 27 = @cast_expr +| 28 = @object_creation_expr +| 29 = @explicit_delegate_creation_expr +| 30 = @implicit_delegate_creation_expr +| 31 = @array_creation_expr +| 32 = @default_expr +| 33 = @plus_expr +| 34 = @minus_expr +| 35 = @bit_not_expr +| 36 = @log_not_expr +| 37 = @post_incr_expr +| 38 = @post_decr_expr +| 39 = @pre_incr_expr +| 40 = @pre_decr_expr +/* multiplicative */ +| 41 = @mul_expr +| 42 = @div_expr +| 43 = @rem_expr +/* additive */ +| 44 = @add_expr +| 45 = @sub_expr +/* shift */ +| 46 = @lshift_expr +| 47 = @rshift_expr +/* relational */ +| 48 = @lt_expr +| 49 = @gt_expr +| 50 = @le_expr +| 51 = @ge_expr +/* equality */ +| 52 = @eq_expr +| 53 = @ne_expr +/* logical */ +| 54 = @bit_and_expr +| 55 = @bit_xor_expr +| 56 = @bit_or_expr +| 57 = @log_and_expr +| 58 = @log_or_expr +/* type testing */ +| 59 = @is_expr +| 60 = @as_expr +/* null coalescing */ +| 61 = @null_coalescing_expr +/* conditional */ +| 62 = @conditional_expr +/* assignment */ +| 63 = @simple_assign_expr +| 64 = @assign_add_expr +| 65 = @assign_sub_expr +| 66 = @assign_mul_expr +| 67 = @assign_div_expr +| 68 = @assign_rem_expr +| 69 = @assign_and_expr +| 70 = @assign_xor_expr +| 71 = @assign_or_expr +| 72 = @assign_lshift_expr +| 73 = @assign_rshift_expr +/* more */ +| 74 = @object_init_expr +| 75 = @collection_init_expr +| 76 = @array_init_expr +| 77 = @checked_expr +| 78 = @unchecked_expr +| 79 = @constructor_init_expr +| 80 = @add_event_expr +| 81 = @remove_event_expr +| 82 = @par_expr +| 83 = @local_var_decl_expr +| 84 = @lambda_expr +| 85 = @anonymous_method_expr +| 86 = @namespace_expr +/* dynamic */ +| 92 = @dynamic_element_access_expr +| 93 = @dynamic_member_access_expr +/* unsafe */ +| 100 = @pointer_indirection_expr +| 101 = @address_of_expr +| 102 = @sizeof_expr +/* async */ +| 103 = @await_expr +/* C# 6.0 */ +| 104 = @nameof_expr +| 105 = @interpolated_string_expr +| 106 = @unknown_expr +/* C# 7.0 */ +| 107 = @throw_expr +| 108 = @tuple_expr +| 109 = @local_function_invocation_expr +| 110 = @ref_expr +| 111 = @discard_expr +/* C# 8.0 */ +| 112 = @range_expr +| 113 = @index_expr +| 114 = @switch_expr +| 115 = @recursive_pattern_expr +| 116 = @property_pattern_expr +| 117 = @positional_pattern_expr +| 118 = @switch_case_expr +| 119 = @assign_coalesce_expr +| 120 = @suppress_nullable_warning_expr +| 121 = @namespace_access_expr +/* C# 9.0 */ +| 122 = @lt_pattern_expr +| 123 = @gt_pattern_expr +| 124 = @le_pattern_expr +| 125 = @ge_pattern_expr +| 126 = @not_pattern_expr +| 127 = @and_pattern_expr +| 128 = @or_pattern_expr +| 129 = @function_pointer_invocation_expr +| 130 = @with_expr +/* C# 11.0 */ +| 131 = @list_pattern_expr +| 132 = @slice_pattern_expr +| 133 = @urshift_expr +| 134 = @assign_urshift_expr +| 135 = @utf8_string_literal_expr +/* C# 12.0 */ +| 136 = @collection_expr +| 137 = @spread_element_expr +| 138 = @interpolated_string_insert_expr +/* Preprocessor */ +| 999 = @define_symbol_expr +; + +@switch = @switch_stmt | @switch_expr; +@case = @case_stmt | @switch_case_expr; +@pattern_match = @case | @is_expr; +@unary_pattern_expr = @not_pattern_expr; +@relational_pattern_expr = @gt_pattern_expr | @lt_pattern_expr | @ge_pattern_expr | @le_pattern_expr; +@binary_pattern_expr = @and_pattern_expr | @or_pattern_expr; + +@integer_literal_expr = @int_literal_expr | @long_literal_expr | @uint_literal_expr | @ulong_literal_expr; +@real_literal_expr = @float_literal_expr | @double_literal_expr | @decimal_literal_expr; +@string_literal_expr = @utf16_string_literal_expr | @utf8_string_literal_expr; +@literal_expr = @bool_literal_expr | @char_literal_expr | @integer_literal_expr | @real_literal_expr + | @string_literal_expr | @null_literal_expr; + +@assign_expr = @simple_assign_expr | @assign_op_expr | @local_var_decl_expr; +@assign_op_call_expr = @assign_arith_expr | @assign_bitwise_expr +@assign_op_expr = @assign_op_call_expr | @assign_event_expr | @assign_coalesce_expr; +@assign_event_expr = @add_event_expr | @remove_event_expr; + +@add_operation = @add_expr | @assign_add_expr; +@sub_operation = @sub_expr | @assign_sub_expr; +@mul_operation = @mul_expr | @assign_mul_expr; +@div_operation = @div_expr | @assign_div_expr; +@rem_operation = @rem_expr | @assign_rem_expr; +@and_operation = @bit_and_expr | @assign_and_expr; +@xor_operation = @bit_xor_expr | @assign_xor_expr; +@or_operation = @bit_or_expr | @assign_or_expr; +@lshift_operation = @lshift_expr | @assign_lshift_expr; +@rshift_operation = @rshift_expr | @assign_rshift_expr; +@urshift_operation = @urshift_expr | @assign_urshift_expr; +@null_coalescing_operation = @null_coalescing_expr | @assign_coalesce_expr; + +@assign_arith_expr = @assign_add_expr | @assign_sub_expr | @assign_mul_expr | @assign_div_expr + | @assign_rem_expr +@assign_bitwise_expr = @assign_and_expr | @assign_or_expr | @assign_xor_expr + | @assign_lshift_expr | @assign_rshift_expr | @assign_urshift_expr; + +@member_access_expr = @field_access_expr | @property_access_expr | @indexer_access_expr | @event_access_expr + | @method_access_expr | @type_access_expr | @dynamic_member_access_expr; +@access_expr = @member_access_expr | @this_access_expr | @base_access_expr | @assignable_access_expr | @namespace_access_expr; +@element_access_expr = @indexer_access_expr | @array_access_expr | @dynamic_element_access_expr; + +@local_variable_access = @local_variable_access_expr | @local_var_decl_expr; +@local_scope_variable_access_expr = @parameter_access_expr | @local_variable_access; +@variable_access_expr = @local_scope_variable_access_expr | @field_access_expr; + +@assignable_access_expr = @variable_access_expr | @property_access_expr | @element_access_expr + | @event_access_expr | @dynamic_member_access_expr; + +@objectorcollection_init_expr = @object_init_expr | @collection_init_expr; + +@delegate_creation_expr = @explicit_delegate_creation_expr | @implicit_delegate_creation_expr; + +@bin_arith_expr = @mul_expr | @div_expr | @rem_expr | @add_expr | @sub_expr; +@bin_arith_operation = @mul_operation | @div_operation | @rem_operation | @add_operation | @sub_operation; + +@incr_operation = @pre_incr_expr | @post_incr_expr; +@decr_operation = @pre_decr_expr | @post_decr_expr; +@mut_operation = @incr_operation | @decr_operation; +@un_arith_operation = @plus_expr | @minus_expr | @mut_operation; +@arith_operation = @bin_arith_operation | @un_arith_operation; + +@ternary_log_operation = @conditional_expr; +@bin_log_operation = @log_and_expr | @log_or_expr | @null_coalescing_operation; +@un_log_operation = @log_not_expr; +@log_operation = @un_log_operation | @bin_log_operation | @ternary_log_operation; + +@bin_bit_expr = @bit_and_expr | @bit_or_expr | @bit_xor_expr | @lshift_expr + | @rshift_expr | @urshift_expr; +@bin_bit_operation = @and_operation | @or_operation | @xor_operation | @lshift_operation + | @rshift_operation | @urshift_operation; +@un_bit_expr = @bit_not_expr; +@un_bit_operation = @un_bit_expr; +@bit_expr = @un_bit_expr | @bin_bit_expr; +@bit_operation = @un_bit_operation | @bin_bit_operation; + +@equality_op_expr = @eq_expr | @ne_expr; +@rel_op_expr = @gt_expr | @lt_expr| @ge_expr | @le_expr; +@comp_expr = @equality_op_expr | @rel_op_expr; + +@operation_expr = @un_operation | @bin_operation | @ternary_operation; + +@ternary_operation = @ternary_log_operation; +@bin_operation = @assign_expr | @bin_arith_operation | @bin_log_operation | @bin_bit_operation | @comp_expr; +@un_operation = @un_arith_operation | @un_log_operation | @un_bit_operation | @sizeof_expr + | @pointer_indirection_expr | @address_of_expr; + +@anonymous_function_expr = @lambda_expr | @anonymous_method_expr; + +@op_invoke_expr = @operator_invocation_expr | @assign_op_call_expr +@call = @method_invocation_expr | @constructor_init_expr | @op_invoke_expr + | @delegate_invocation_expr | @object_creation_expr | @call_access_expr + | @local_function_invocation_expr | @function_pointer_invocation_expr; + +@call_access_expr = @property_access_expr | @event_access_expr | @indexer_access_expr; + +@late_bindable_expr = @dynamic_element_access_expr | @dynamic_member_access_expr + | @object_creation_expr | @method_invocation_expr | @op_invoke_expr; + +@throw_element = @throw_expr | @throw_stmt; + +@implicitly_typeable_object_creation_expr = @object_creation_expr | @explicit_delegate_creation_expr; + +implicitly_typed_array_creation( + unique int id: @array_creation_expr ref); + +explicitly_sized_array_creation( + unique int id: @array_creation_expr ref); + +stackalloc_array_creation( + unique int id: @array_creation_expr ref); + +implicitly_typed_object_creation( + unique int id: @implicitly_typeable_object_creation_expr ref); + +mutator_invocation_mode( + unique int id: @operator_invocation_expr ref, + int mode: int ref /* prefix = 1, postfix = 2*/); + +expr_value( + unique int id: @expr ref, + string value: string ref); + +expr_call( + unique int caller_id: @expr ref, + int target_id: @callable ref); + +expr_access( + unique int accesser_id: @access_expr ref, + int target_id: @accessible ref); + +@accessible = @method | @assignable | @local_function | @namespace; + +expr_location( + unique int id: @expr ref, + int loc: @location ref); + +dynamic_member_name( + unique int id: @late_bindable_expr ref, + string name: string ref); + +@qualifiable_expr = @member_access_expr + | @method_invocation_expr + | @element_access_expr + | @assign_op_call_expr; + +conditional_access( + unique int id: @qualifiable_expr ref); + +expr_argument( + unique int id: @expr ref, + int mode: int ref); + /* mode is the same as params: value = 0, ref = 1, out = 2 */ + +expr_argument_name( + unique int id: @expr ref, + string name: string ref); + +lambda_expr_return_type( + unique int id: @lambda_expr ref, + int type_id: @type_or_ref ref); + +/* Compiler generated */ + +compiler_generated(unique int id: @element ref); + +/** CONTROL/DATA FLOW **/ + +@control_flow_element = @stmt | @expr | @parameter; + +/* XML Files */ + +xmlEncoding ( + unique int id: @file ref, + string encoding: string ref); + +xmlDTDs( + unique int id: @xmldtd, + string root: string ref, + string publicId: string ref, + string systemId: string ref, + int fileid: @file ref); + +xmlElements( + unique int id: @xmlelement, + string name: string ref, + int parentid: @xmlparent ref, + int idx: int ref, + int fileid: @file ref); + +xmlAttrs( + unique int id: @xmlattribute, + int elementid: @xmlelement ref, + string name: string ref, + string value: string ref, + int idx: int ref, + int fileid: @file ref); + +xmlNs( + int id: @xmlnamespace, + string prefixName: string ref, + string URI: string ref, + int fileid: @file ref); + +xmlHasNs( + int elementId: @xmlnamespaceable ref, + int nsId: @xmlnamespace ref, + int fileid: @file ref); + +xmlComments( + unique int id: @xmlcomment, + string text: string ref, + int parentid: @xmlparent ref, + int fileid: @file ref); + +xmlChars( + unique int id: @xmlcharacters, + string text: string ref, + int parentid: @xmlparent ref, + int idx: int ref, + int isCDATA: int ref, + int fileid: @file ref); + +@xmlparent = @file | @xmlelement; +@xmlnamespaceable = @xmlelement | @xmlattribute; + +xmllocations( + int xmlElement: @xmllocatable ref, + int location: @location_default ref); + +@xmllocatable = @xmlcharacters | @xmlelement | @xmlcomment | @xmlattribute | @xmldtd | @file | @xmlnamespace; + +/* Comments */ + +commentline( + unique int id: @commentline, + int kind: int ref, + string text: string ref, + string rawtext: string ref); + +case @commentline.kind of + 0 = @singlelinecomment +| 1 = @xmldoccomment +| 2 = @multilinecomment; + +commentline_location( + unique int id: @commentline ref, + int loc: @location ref); + +commentblock( + unique int id : @commentblock); + +commentblock_location( + unique int id: @commentblock ref, + int loc: @location ref); + +commentblock_binding( + int id: @commentblock ref, + int entity: @element ref, + int bindtype: int ref); /* 0: Parent, 1: Best, 2: Before, 3: After */ + +commentblock_child( + int id: @commentblock ref, + int commentline: @commentline ref, + int index: int ref); + +/* ASP.NET */ + +case @asp_element.kind of + 0=@asp_close_tag +| 1=@asp_code +| 2=@asp_comment +| 3=@asp_data_binding +| 4=@asp_directive +| 5=@asp_open_tag +| 6=@asp_quoted_string +| 7=@asp_text +| 8=@asp_xml_directive; + +@asp_attribute = @asp_code | @asp_data_binding | @asp_quoted_string; + +asp_elements( + unique int id: @asp_element, + int kind: int ref, + int loc: @location ref); + +asp_comment_server(unique int comment: @asp_comment ref); +asp_code_inline(unique int code: @asp_code ref); +asp_directive_attribute( + int directive: @asp_directive ref, + int index: int ref, + string name: string ref, + int value: @asp_quoted_string ref); +asp_directive_name( + unique int directive: @asp_directive ref, + string name: string ref); +asp_element_body( + unique int element: @asp_element ref, + string body: string ref); +asp_tag_attribute( + int tag: @asp_open_tag ref, + int index: int ref, + string name: string ref, + int attribute: @asp_attribute ref); +asp_tag_name( + unique int tag: @asp_open_tag ref, + string name: string ref); +asp_tag_isempty(int tag: @asp_open_tag ref); diff --git a/csharp/ql/lib/upgrades/3cabc77473cbbda95edebafea345c2e3fdfa12d9/upgrade.properties b/csharp/ql/lib/upgrades/3cabc77473cbbda95edebafea345c2e3fdfa12d9/upgrade.properties new file mode 100644 index 00000000000..85b8a1e6c23 --- /dev/null +++ b/csharp/ql/lib/upgrades/3cabc77473cbbda95edebafea345c2e3fdfa12d9/upgrade.properties @@ -0,0 +1,2 @@ +description: Restructure and rename types related to operations. +compatibility: full diff --git a/csharp/ql/src/Telemetry/DatabaseQuality.qll b/csharp/ql/src/Telemetry/DatabaseQuality.qll index ad7ac682bf5..a26993905de 100644 --- a/csharp/ql/src/Telemetry/DatabaseQuality.qll +++ b/csharp/ql/src/Telemetry/DatabaseQuality.qll @@ -63,7 +63,7 @@ module CallTargetStats implements StatsSig { additional predicate isNotOkCall(Call c) { not exists(c.getTarget()) and - not c instanceof DelegateCall and + not c instanceof DelegateLikeCall and not c instanceof DynamicExpr and not isNoSetterPropertyCallInConstructor(c) and not isNoSetterPropertyInitialization(c) and diff --git a/csharp/ql/test/library-tests/csharp11/operators.expected b/csharp/ql/test/library-tests/csharp11/operators.expected index 177019a3ea0..dfd131dbfa9 100644 --- a/csharp/ql/test/library-tests/csharp11/operators.expected +++ b/csharp/ql/test/library-tests/csharp11/operators.expected @@ -1,6 +1,7 @@ binarybitwise | Operators.cs:7:18:7:25 | ... >>> ... | Operators.cs:7:18:7:19 | access to local variable x1 | Operators.cs:7:25:7:25 | 2 | >>> | UnsignedRightShiftExpr | | Operators.cs:10:18:10:25 | ... >>> ... | Operators.cs:10:18:10:19 | access to local variable y1 | Operators.cs:10:25:10:25 | 3 | >>> | UnsignedRightShiftExpr | +| Operators.cs:13:9:13:16 | ... >>>= ... | Operators.cs:13:9:13:9 | access to local variable z | Operators.cs:13:16:13:16 | 5 | >>>= | AssignUnsignedRightShiftExpr | assignbitwise | Operators.cs:13:9:13:16 | ... >>>= ... | Operators.cs:13:9:13:9 | access to local variable z | Operators.cs:13:16:13:16 | 5 | >>>= | AssignUnsignedRightShiftExpr | userdefined diff --git a/csharp/ql/test/library-tests/csharp11/operators.ql b/csharp/ql/test/library-tests/csharp11/operators.ql index f1543e2d744..da14d2b6cb7 100644 --- a/csharp/ql/test/library-tests/csharp11/operators.ql +++ b/csharp/ql/test/library-tests/csharp11/operators.ql @@ -11,7 +11,7 @@ query predicate binarybitwise( } query predicate assignbitwise( - AssignBitwiseOperation op, Expr left, Expr right, string name, string qlclass + AssignBitwiseExpr op, Expr left, Expr right, string name, string qlclass ) { op.getFile().getStem() = "Operators" and left = op.getLeftOperand() and diff --git a/csharp/ql/test/library-tests/dataflow/external-models/ExternalFlow.cs b/csharp/ql/test/library-tests/dataflow/external-models/ExternalFlow.cs index 1fa43ba456e..bf731715abf 100644 --- a/csharp/ql/test/library-tests/dataflow/external-models/ExternalFlow.cs +++ b/csharp/ql/test/library-tests/dataflow/external-models/ExternalFlow.cs @@ -442,4 +442,31 @@ namespace My.Qltest static void Sink(object o) { } } + + // Test operator overloads + public class N + { + public void operator +=(N y) => throw null; + + public void operator checked +=(N y) => throw null; + + public void M1(N n) + { + var n0 = new N(); + n += n0; + Sink(n); + } + + public void M2(N n) + { + var n0 = new N(); + checked + { + n += n0; + } + Sink(n); + } + + static void Sink(object o) { } + } } diff --git a/csharp/ql/test/library-tests/dataflow/external-models/ExternalFlow.expected b/csharp/ql/test/library-tests/dataflow/external-models/ExternalFlow.expected index b0256d6c41d..62bf675dc60 100644 --- a/csharp/ql/test/library-tests/dataflow/external-models/ExternalFlow.expected +++ b/csharp/ql/test/library-tests/dataflow/external-models/ExternalFlow.expected @@ -32,14 +32,16 @@ models | 31 | Summary: My.Qltest; Library; false; GetValue; (); ; Argument[this].SyntheticField[X]; ReturnValue; value; dfc-generated | | 32 | Summary: My.Qltest; Library; false; MixedFlowArgs; (System.Object,System.Object); ; Argument[1]; ReturnValue; value; manual | | 33 | Summary: My.Qltest; Library; false; SetValue; (System.Object); ; Argument[0]; Argument[this].SyntheticField[X]; value; dfc-generated | -| 34 | Summary: My.Qltest; TestExtensions+extension(System.Object); false; Method1; (System.Object); ; Argument[0]; ReturnValue; value; manual | -| 35 | Summary: My.Qltest; TestExtensions+extension(System.Object); false; StaticMethod1; (System.Object); ; Argument[0]; ReturnValue; value; manual | -| 36 | Summary: My.Qltest; TestExtensions+extension(System.Object); false; get_Property1; (System.Object); ; Argument[0].SyntheticField[TestExtensions.Property1]; ReturnValue; value; manual | -| 37 | Summary: My.Qltest; TestExtensions+extension(System.Object); false; set_Property1; (System.Object,System.Object); ; Argument[1]; Argument[0].SyntheticField[TestExtensions.Property1]; value; manual | -| 38 | Summary: My.Qltest; TestExtensions+extension(T); false; GenericMethod1; (T); ; Argument[0]; ReturnValue; value; manual | -| 39 | Summary: My.Qltest; TestExtensions+extension(T); false; GenericStaticMethod1; (T); ; Argument[0]; ReturnValue; value; manual | -| 40 | Summary: My.Qltest; TestExtensions+extension(T); false; get_GenericProperty1; (T); ; Argument[0].SyntheticField[TestExtensions.GenericProperty1]; ReturnValue; value; manual | -| 41 | Summary: My.Qltest; TestExtensions+extension(T); false; set_GenericProperty1; (T,T); ; Argument[1]; Argument[0].SyntheticField[TestExtensions.GenericProperty1]; value; manual | +| 34 | Summary: My.Qltest; N; false; op_AdditionAssignment; (My.Qltest.N); ; Argument[0]; Argument[this]; taint; manual | +| 35 | Summary: My.Qltest; N; false; op_CheckedAdditionAssignment; (My.Qltest.N); ; Argument[0]; Argument[this]; taint; manual | +| 36 | Summary: My.Qltest; TestExtensions+extension(System.Object); false; Method1; (System.Object); ; Argument[0]; ReturnValue; value; manual | +| 37 | Summary: My.Qltest; TestExtensions+extension(System.Object); false; StaticMethod1; (System.Object); ; Argument[0]; ReturnValue; value; manual | +| 38 | Summary: My.Qltest; TestExtensions+extension(System.Object); false; get_Property1; (System.Object); ; Argument[0].SyntheticField[TestExtensions.Property1]; ReturnValue; value; manual | +| 39 | Summary: My.Qltest; TestExtensions+extension(System.Object); false; set_Property1; (System.Object,System.Object); ; Argument[1]; Argument[0].SyntheticField[TestExtensions.Property1]; value; manual | +| 40 | Summary: My.Qltest; TestExtensions+extension(T); false; GenericMethod1; (T); ; Argument[0]; ReturnValue; value; manual | +| 41 | Summary: My.Qltest; TestExtensions+extension(T); false; GenericStaticMethod1; (T); ; Argument[0]; ReturnValue; value; manual | +| 42 | Summary: My.Qltest; TestExtensions+extension(T); false; get_GenericProperty1; (T); ; Argument[0].SyntheticField[TestExtensions.GenericProperty1]; ReturnValue; value; manual | +| 43 | Summary: My.Qltest; TestExtensions+extension(T); false; set_GenericProperty1; (T,T); ; Argument[1]; Argument[0].SyntheticField[TestExtensions.GenericProperty1]; value; manual | edges | ExternalFlow.cs:9:20:9:23 | access to local variable arg1 : Object | ExternalFlow.cs:10:29:10:32 | access to local variable arg1 : Object | provenance | | | ExternalFlow.cs:9:27:9:38 | object creation of type Object : Object | ExternalFlow.cs:9:20:9:23 | access to local variable arg1 : Object | provenance | | @@ -162,69 +164,77 @@ edges | ExternalFlow.cs:373:17:373:19 | access to local variable obj : Object | ExternalFlow.cs:377:45:377:47 | access to local variable obj : Object | provenance | | | ExternalFlow.cs:373:23:373:34 | object creation of type Object : Object | ExternalFlow.cs:373:17:373:19 | access to local variable obj : Object | provenance | | | ExternalFlow.cs:374:17:374:18 | access to local variable o1 : Object | ExternalFlow.cs:375:18:375:19 | access to local variable o1 | provenance | | -| ExternalFlow.cs:374:22:374:24 | access to local variable obj : Object | ExternalFlow.cs:374:22:374:34 | call to method Method1 : Object | provenance | MaD:34 | +| ExternalFlow.cs:374:22:374:24 | access to local variable obj : Object | ExternalFlow.cs:374:22:374:34 | call to method Method1 : Object | provenance | MaD:36 | | ExternalFlow.cs:374:22:374:34 | call to method Method1 : Object | ExternalFlow.cs:374:17:374:18 | access to local variable o1 : Object | provenance | | | ExternalFlow.cs:377:17:377:18 | access to local variable o2 : Object | ExternalFlow.cs:378:18:378:19 | access to local variable o2 | provenance | | | ExternalFlow.cs:377:22:377:48 | call to method Method1 : Object | ExternalFlow.cs:377:17:377:18 | access to local variable o2 : Object | provenance | | -| ExternalFlow.cs:377:45:377:47 | access to local variable obj : Object | ExternalFlow.cs:377:22:377:48 | call to method Method1 : Object | provenance | MaD:34 | +| ExternalFlow.cs:377:45:377:47 | access to local variable obj : Object | ExternalFlow.cs:377:22:377:48 | call to method Method1 : Object | provenance | MaD:36 | | ExternalFlow.cs:383:17:383:19 | access to local variable obj : Object | ExternalFlow.cs:384:43:384:45 | access to local variable obj : Object | provenance | | | ExternalFlow.cs:383:17:383:19 | access to local variable obj : Object | ExternalFlow.cs:387:51:387:53 | access to local variable obj : Object | provenance | | | ExternalFlow.cs:383:23:383:34 | object creation of type Object : Object | ExternalFlow.cs:383:17:383:19 | access to local variable obj : Object | provenance | | | ExternalFlow.cs:384:17:384:18 | access to local variable o1 : Object | ExternalFlow.cs:385:18:385:19 | access to local variable o1 | provenance | | | ExternalFlow.cs:384:22:384:46 | call to method StaticMethod1 : Object | ExternalFlow.cs:384:17:384:18 | access to local variable o1 : Object | provenance | | -| ExternalFlow.cs:384:43:384:45 | access to local variable obj : Object | ExternalFlow.cs:384:22:384:46 | call to method StaticMethod1 : Object | provenance | MaD:35 | +| ExternalFlow.cs:384:43:384:45 | access to local variable obj : Object | ExternalFlow.cs:384:22:384:46 | call to method StaticMethod1 : Object | provenance | MaD:37 | | ExternalFlow.cs:387:17:387:18 | access to local variable o2 : Object | ExternalFlow.cs:388:18:388:19 | access to local variable o2 | provenance | | | ExternalFlow.cs:387:22:387:54 | call to method StaticMethod1 : Object | ExternalFlow.cs:387:17:387:18 | access to local variable o2 : Object | provenance | | -| ExternalFlow.cs:387:51:387:53 | access to local variable obj : Object | ExternalFlow.cs:387:22:387:54 | call to method StaticMethod1 : Object | provenance | MaD:35 | +| ExternalFlow.cs:387:51:387:53 | access to local variable obj : Object | ExternalFlow.cs:387:22:387:54 | call to method StaticMethod1 : Object | provenance | MaD:37 | | ExternalFlow.cs:393:17:393:19 | access to local variable obj : Object | ExternalFlow.cs:394:27:394:29 | access to local variable obj : Object | provenance | | | ExternalFlow.cs:393:23:393:34 | object creation of type Object : Object | ExternalFlow.cs:393:17:393:19 | access to local variable obj : Object | provenance | | | ExternalFlow.cs:394:13:394:13 | [post] access to parameter o : Object [synthetic TestExtensions.Property1] : Object | ExternalFlow.cs:395:22:395:22 | access to parameter o : Object [synthetic TestExtensions.Property1] : Object | provenance | | -| ExternalFlow.cs:394:27:394:29 | access to local variable obj : Object | ExternalFlow.cs:394:13:394:13 | [post] access to parameter o : Object [synthetic TestExtensions.Property1] : Object | provenance | MaD:37 | +| ExternalFlow.cs:394:27:394:29 | access to local variable obj : Object | ExternalFlow.cs:394:13:394:13 | [post] access to parameter o : Object [synthetic TestExtensions.Property1] : Object | provenance | MaD:39 | | ExternalFlow.cs:395:17:395:18 | access to local variable o1 : Object | ExternalFlow.cs:396:18:396:19 | access to local variable o1 | provenance | | -| ExternalFlow.cs:395:22:395:22 | access to parameter o : Object [synthetic TestExtensions.Property1] : Object | ExternalFlow.cs:395:22:395:32 | access to property Property1 : Object | provenance | MaD:36 | +| ExternalFlow.cs:395:22:395:22 | access to parameter o : Object [synthetic TestExtensions.Property1] : Object | ExternalFlow.cs:395:22:395:32 | access to property Property1 : Object | provenance | MaD:38 | | ExternalFlow.cs:395:22:395:32 | access to property Property1 : Object | ExternalFlow.cs:395:17:395:18 | access to local variable o1 : Object | provenance | | | ExternalFlow.cs:401:17:401:19 | access to local variable obj : Object | ExternalFlow.cs:402:45:402:47 | access to local variable obj : Object | provenance | | | ExternalFlow.cs:401:23:401:34 | object creation of type Object : Object | ExternalFlow.cs:401:17:401:19 | access to local variable obj : Object | provenance | | | ExternalFlow.cs:402:42:402:42 | [post] access to parameter o : Object [synthetic TestExtensions.Property1] : Object | ExternalFlow.cs:403:51:403:51 | access to parameter o : Object [synthetic TestExtensions.Property1] : Object | provenance | | -| ExternalFlow.cs:402:45:402:47 | access to local variable obj : Object | ExternalFlow.cs:402:42:402:42 | [post] access to parameter o : Object [synthetic TestExtensions.Property1] : Object | provenance | MaD:37 | +| ExternalFlow.cs:402:45:402:47 | access to local variable obj : Object | ExternalFlow.cs:402:42:402:42 | [post] access to parameter o : Object [synthetic TestExtensions.Property1] : Object | provenance | MaD:39 | | ExternalFlow.cs:403:17:403:18 | access to local variable o1 : Object | ExternalFlow.cs:404:18:404:19 | access to local variable o1 | provenance | | | ExternalFlow.cs:403:22:403:52 | call to extension accessor get_Property1 : Object | ExternalFlow.cs:403:17:403:18 | access to local variable o1 : Object | provenance | | -| ExternalFlow.cs:403:51:403:51 | access to parameter o : Object [synthetic TestExtensions.Property1] : Object | ExternalFlow.cs:403:22:403:52 | call to extension accessor get_Property1 : Object | provenance | MaD:36 | +| ExternalFlow.cs:403:51:403:51 | access to parameter o : Object [synthetic TestExtensions.Property1] : Object | ExternalFlow.cs:403:22:403:52 | call to extension accessor get_Property1 : Object | provenance | MaD:38 | | ExternalFlow.cs:409:17:409:19 | access to local variable obj : Object | ExternalFlow.cs:410:22:410:24 | access to local variable obj : Object | provenance | | | ExternalFlow.cs:409:17:409:19 | access to local variable obj : Object | ExternalFlow.cs:413:52:413:54 | access to local variable obj : Object | provenance | | | ExternalFlow.cs:409:23:409:34 | object creation of type Object : Object | ExternalFlow.cs:409:17:409:19 | access to local variable obj : Object | provenance | | | ExternalFlow.cs:410:17:410:18 | access to local variable o1 : Object | ExternalFlow.cs:411:18:411:19 | access to local variable o1 | provenance | | -| ExternalFlow.cs:410:22:410:24 | access to local variable obj : Object | ExternalFlow.cs:410:22:410:41 | call to method GenericMethod1 : Object | provenance | MaD:38 | +| ExternalFlow.cs:410:22:410:24 | access to local variable obj : Object | ExternalFlow.cs:410:22:410:41 | call to method GenericMethod1 : Object | provenance | MaD:40 | | ExternalFlow.cs:410:22:410:41 | call to method GenericMethod1 : Object | ExternalFlow.cs:410:17:410:18 | access to local variable o1 : Object | provenance | | | ExternalFlow.cs:413:17:413:18 | access to local variable o2 : Object | ExternalFlow.cs:414:18:414:19 | access to local variable o2 | provenance | | | ExternalFlow.cs:413:22:413:55 | call to method GenericMethod1 : Object | ExternalFlow.cs:413:17:413:18 | access to local variable o2 : Object | provenance | | -| ExternalFlow.cs:413:52:413:54 | access to local variable obj : Object | ExternalFlow.cs:413:22:413:55 | call to method GenericMethod1 : Object | provenance | MaD:38 | +| ExternalFlow.cs:413:52:413:54 | access to local variable obj : Object | ExternalFlow.cs:413:22:413:55 | call to method GenericMethod1 : Object | provenance | MaD:40 | | ExternalFlow.cs:419:17:419:19 | access to local variable obj : Object | ExternalFlow.cs:420:50:420:52 | access to local variable obj : Object | provenance | | | ExternalFlow.cs:419:17:419:19 | access to local variable obj : Object | ExternalFlow.cs:423:58:423:60 | access to local variable obj : Object | provenance | | | ExternalFlow.cs:419:23:419:34 | object creation of type Object : Object | ExternalFlow.cs:419:17:419:19 | access to local variable obj : Object | provenance | | | ExternalFlow.cs:420:17:420:18 | access to local variable o1 : Object | ExternalFlow.cs:421:18:421:19 | access to local variable o1 | provenance | | | ExternalFlow.cs:420:22:420:53 | call to method GenericStaticMethod1 : Object | ExternalFlow.cs:420:17:420:18 | access to local variable o1 : Object | provenance | | -| ExternalFlow.cs:420:50:420:52 | access to local variable obj : Object | ExternalFlow.cs:420:22:420:53 | call to method GenericStaticMethod1 : Object | provenance | MaD:39 | +| ExternalFlow.cs:420:50:420:52 | access to local variable obj : Object | ExternalFlow.cs:420:22:420:53 | call to method GenericStaticMethod1 : Object | provenance | MaD:41 | | ExternalFlow.cs:423:17:423:18 | access to local variable o2 : Object | ExternalFlow.cs:424:18:424:19 | access to local variable o2 | provenance | | | ExternalFlow.cs:423:22:423:61 | call to method GenericStaticMethod1 : Object | ExternalFlow.cs:423:17:423:18 | access to local variable o2 : Object | provenance | | -| ExternalFlow.cs:423:58:423:60 | access to local variable obj : Object | ExternalFlow.cs:423:22:423:61 | call to method GenericStaticMethod1 : Object | provenance | MaD:39 | +| ExternalFlow.cs:423:58:423:60 | access to local variable obj : Object | ExternalFlow.cs:423:22:423:61 | call to method GenericStaticMethod1 : Object | provenance | MaD:41 | | ExternalFlow.cs:429:17:429:19 | access to local variable obj : Object | ExternalFlow.cs:430:34:430:36 | access to local variable obj : Object | provenance | | | ExternalFlow.cs:429:23:429:34 | object creation of type Object : Object | ExternalFlow.cs:429:17:429:19 | access to local variable obj : Object | provenance | | | ExternalFlow.cs:430:13:430:13 | [post] access to parameter o : Object [property GenericProperty1] : Object | ExternalFlow.cs:431:22:431:22 | access to parameter o : Object [property GenericProperty1] : Object | provenance | | | ExternalFlow.cs:430:13:430:13 | [post] access to parameter o : Object [synthetic TestExtensions.GenericProperty1] : Object | ExternalFlow.cs:431:22:431:22 | access to parameter o : Object [synthetic TestExtensions.GenericProperty1] : Object | provenance | | | ExternalFlow.cs:430:34:430:36 | access to local variable obj : Object | ExternalFlow.cs:430:13:430:13 | [post] access to parameter o : Object [property GenericProperty1] : Object | provenance | | -| ExternalFlow.cs:430:34:430:36 | access to local variable obj : Object | ExternalFlow.cs:430:13:430:13 | [post] access to parameter o : Object [synthetic TestExtensions.GenericProperty1] : Object | provenance | MaD:41 | +| ExternalFlow.cs:430:34:430:36 | access to local variable obj : Object | ExternalFlow.cs:430:13:430:13 | [post] access to parameter o : Object [synthetic TestExtensions.GenericProperty1] : Object | provenance | MaD:43 | | ExternalFlow.cs:431:17:431:18 | access to local variable o1 : Object | ExternalFlow.cs:432:18:432:19 | access to local variable o1 | provenance | | | ExternalFlow.cs:431:22:431:22 | access to parameter o : Object [property GenericProperty1] : Object | ExternalFlow.cs:431:22:431:39 | access to property GenericProperty1 : Object | provenance | | -| ExternalFlow.cs:431:22:431:22 | access to parameter o : Object [synthetic TestExtensions.GenericProperty1] : Object | ExternalFlow.cs:431:22:431:39 | access to property GenericProperty1 : Object | provenance | MaD:40 | +| ExternalFlow.cs:431:22:431:22 | access to parameter o : Object [synthetic TestExtensions.GenericProperty1] : Object | ExternalFlow.cs:431:22:431:39 | access to property GenericProperty1 : Object | provenance | MaD:42 | | ExternalFlow.cs:431:22:431:39 | access to property GenericProperty1 : Object | ExternalFlow.cs:431:17:431:18 | access to local variable o1 : Object | provenance | | | ExternalFlow.cs:437:17:437:19 | access to local variable obj : Object | ExternalFlow.cs:438:52:438:54 | access to local variable obj : Object | provenance | | | ExternalFlow.cs:437:23:437:34 | object creation of type Object : Object | ExternalFlow.cs:437:17:437:19 | access to local variable obj : Object | provenance | | | ExternalFlow.cs:438:49:438:49 | [post] access to parameter o : Object [synthetic TestExtensions.GenericProperty1] : Object | ExternalFlow.cs:439:58:439:58 | access to parameter o : Object [synthetic TestExtensions.GenericProperty1] : Object | provenance | | -| ExternalFlow.cs:438:52:438:54 | access to local variable obj : Object | ExternalFlow.cs:438:49:438:49 | [post] access to parameter o : Object [synthetic TestExtensions.GenericProperty1] : Object | provenance | MaD:41 | +| ExternalFlow.cs:438:52:438:54 | access to local variable obj : Object | ExternalFlow.cs:438:49:438:49 | [post] access to parameter o : Object [synthetic TestExtensions.GenericProperty1] : Object | provenance | MaD:43 | | ExternalFlow.cs:439:17:439:18 | access to local variable o1 : Object | ExternalFlow.cs:440:18:440:19 | access to local variable o1 | provenance | | | ExternalFlow.cs:439:22:439:59 | call to extension accessor get_GenericProperty1 : Object | ExternalFlow.cs:439:17:439:18 | access to local variable o1 : Object | provenance | | -| ExternalFlow.cs:439:58:439:58 | access to parameter o : Object [synthetic TestExtensions.GenericProperty1] : Object | ExternalFlow.cs:439:22:439:59 | call to extension accessor get_GenericProperty1 : Object | provenance | MaD:40 | +| ExternalFlow.cs:439:58:439:58 | access to parameter o : Object [synthetic TestExtensions.GenericProperty1] : Object | ExternalFlow.cs:439:22:439:59 | call to extension accessor get_GenericProperty1 : Object | provenance | MaD:42 | +| ExternalFlow.cs:455:17:455:18 | access to local variable n0 : N | ExternalFlow.cs:456:18:456:19 | access to local variable n0 : N | provenance | | +| ExternalFlow.cs:455:22:455:28 | object creation of type N : N | ExternalFlow.cs:455:17:455:18 | access to local variable n0 : N | provenance | | +| ExternalFlow.cs:456:13:456:13 | [post] access to parameter n : N | ExternalFlow.cs:457:18:457:18 | access to parameter n | provenance | | +| ExternalFlow.cs:456:18:456:19 | access to local variable n0 : N | ExternalFlow.cs:456:13:456:13 | [post] access to parameter n : N | provenance | MaD:34 | +| ExternalFlow.cs:462:17:462:18 | access to local variable n0 : N | ExternalFlow.cs:465:22:465:23 | access to local variable n0 : N | provenance | | +| ExternalFlow.cs:462:22:462:28 | object creation of type N : N | ExternalFlow.cs:462:17:462:18 | access to local variable n0 : N | provenance | | +| ExternalFlow.cs:465:17:465:17 | [post] access to parameter n : N | ExternalFlow.cs:467:18:467:18 | access to parameter n | provenance | | +| ExternalFlow.cs:465:22:465:23 | access to local variable n0 : N | ExternalFlow.cs:465:17:465:17 | [post] access to parameter n : N | provenance | MaD:35 | nodes | ExternalFlow.cs:9:20:9:23 | access to local variable arg1 : Object | semmle.label | access to local variable arg1 : Object | | ExternalFlow.cs:9:27:9:38 | object creation of type Object : Object | semmle.label | object creation of type Object : Object | @@ -443,6 +453,16 @@ nodes | ExternalFlow.cs:439:22:439:59 | call to extension accessor get_GenericProperty1 : Object | semmle.label | call to extension accessor get_GenericProperty1 : Object | | ExternalFlow.cs:439:58:439:58 | access to parameter o : Object [synthetic TestExtensions.GenericProperty1] : Object | semmle.label | access to parameter o : Object [synthetic TestExtensions.GenericProperty1] : Object | | ExternalFlow.cs:440:18:440:19 | access to local variable o1 | semmle.label | access to local variable o1 | +| ExternalFlow.cs:455:17:455:18 | access to local variable n0 : N | semmle.label | access to local variable n0 : N | +| ExternalFlow.cs:455:22:455:28 | object creation of type N : N | semmle.label | object creation of type N : N | +| ExternalFlow.cs:456:13:456:13 | [post] access to parameter n : N | semmle.label | [post] access to parameter n : N | +| ExternalFlow.cs:456:18:456:19 | access to local variable n0 : N | semmle.label | access to local variable n0 : N | +| ExternalFlow.cs:457:18:457:18 | access to parameter n | semmle.label | access to parameter n | +| ExternalFlow.cs:462:17:462:18 | access to local variable n0 : N | semmle.label | access to local variable n0 : N | +| ExternalFlow.cs:462:22:462:28 | object creation of type N : N | semmle.label | object creation of type N : N | +| ExternalFlow.cs:465:17:465:17 | [post] access to parameter n : N | semmle.label | [post] access to parameter n : N | +| ExternalFlow.cs:465:22:465:23 | access to local variable n0 : N | semmle.label | access to local variable n0 : N | +| ExternalFlow.cs:467:18:467:18 | access to parameter n | semmle.label | access to parameter n | subpaths | ExternalFlow.cs:84:29:84:32 | access to local variable objs : null [element] : Object | ExternalFlow.cs:84:35:84:35 | o : Object | ExternalFlow.cs:84:40:84:40 | access to parameter o : Object | ExternalFlow.cs:84:25:84:41 | call to method Map : T[] [element] : Object | invalidModelRow @@ -489,3 +509,5 @@ invalidModelRow | ExternalFlow.cs:424:18:424:19 | access to local variable o2 | ExternalFlow.cs:419:23:419:34 | object creation of type Object : Object | ExternalFlow.cs:424:18:424:19 | access to local variable o2 | $@ | ExternalFlow.cs:419:23:419:34 | object creation of type Object : Object | object creation of type Object : Object | | ExternalFlow.cs:432:18:432:19 | access to local variable o1 | ExternalFlow.cs:429:23:429:34 | object creation of type Object : Object | ExternalFlow.cs:432:18:432:19 | access to local variable o1 | $@ | ExternalFlow.cs:429:23:429:34 | object creation of type Object : Object | object creation of type Object : Object | | ExternalFlow.cs:440:18:440:19 | access to local variable o1 | ExternalFlow.cs:437:23:437:34 | object creation of type Object : Object | ExternalFlow.cs:440:18:440:19 | access to local variable o1 | $@ | ExternalFlow.cs:437:23:437:34 | object creation of type Object : Object | object creation of type Object : Object | +| ExternalFlow.cs:457:18:457:18 | access to parameter n | ExternalFlow.cs:455:22:455:28 | object creation of type N : N | ExternalFlow.cs:457:18:457:18 | access to parameter n | $@ | ExternalFlow.cs:455:22:455:28 | object creation of type N : N | object creation of type N : N | +| ExternalFlow.cs:467:18:467:18 | access to parameter n | ExternalFlow.cs:462:22:462:28 | object creation of type N : N | ExternalFlow.cs:467:18:467:18 | access to parameter n | $@ | ExternalFlow.cs:462:22:462:28 | object creation of type N : N | object creation of type N : N | diff --git a/csharp/ql/test/library-tests/dataflow/external-models/ExternalFlow.ext.yml b/csharp/ql/test/library-tests/dataflow/external-models/ExternalFlow.ext.yml index 21e66b84066..9fe50b16354 100644 --- a/csharp/ql/test/library-tests/dataflow/external-models/ExternalFlow.ext.yml +++ b/csharp/ql/test/library-tests/dataflow/external-models/ExternalFlow.ext.yml @@ -53,6 +53,8 @@ extensions: - ["My.Qltest", "TestExtensions+extension(T)", false, "GenericStaticMethod1", "(T)", "", "Argument[0]", "ReturnValue", "value", "manual"] - ["My.Qltest", "TestExtensions+extension(T)", false, "get_GenericProperty1", "(T)", "", "Argument[0].SyntheticField[TestExtensions.GenericProperty1]", "ReturnValue", "value", "manual"] - ["My.Qltest", "TestExtensions+extension(T)", false, "set_GenericProperty1", "(T,T)", "", "Argument[1]", "Argument[0].SyntheticField[TestExtensions.GenericProperty1]", "value", "manual"] + - ["My.Qltest", "N", false, "op_AdditionAssignment", "(My.Qltest.N)", "", "Argument[0]", "Argument[this]", "taint", "manual"] + - ["My.Qltest", "N", false, "op_CheckedAdditionAssignment", "(My.Qltest.N)", "", "Argument[0]", "Argument[this]", "taint", "manual"] - addsTo: pack: codeql/csharp-all diff --git a/csharp/ql/test/library-tests/dataflow/flowsources/aspremote/AspRemoteFlowSource.cs b/csharp/ql/test/library-tests/dataflow/flowsources/aspremote/AspRemoteFlowSource.cs index e554f25f206..5bc8025f231 100644 --- a/csharp/ql/test/library-tests/dataflow/flowsources/aspremote/AspRemoteFlowSource.cs +++ b/csharp/ql/test/library-tests/dataflow/flowsources/aspremote/AspRemoteFlowSource.cs @@ -63,4 +63,32 @@ namespace Testing { public void MyActionMethod(string param) { } } + + // Razor Page handler tests + public class MyPageModel : Microsoft.AspNetCore.Mvc.RazorPages.PageModel + { + // Handler method parameters are remote flow sources + public void OnGet(string id) { } + + public void OnPost(string command, int count) { } + + public void OnPostAsync(string data) { } + + public void OnPut(string value) { } + + public void OnDelete(string itemId) { } + + // Not a handler method — does not start with "On", so not a flow source + public void GetUser(string userId) { } + + // Excluded by [NonHandler] attribute, so not a flow source + [Microsoft.AspNetCore.Mvc.RazorPages.NonHandlerAttribute] + public void OnGetNonHandler(string param) { } + } + + // Subclass of a PageModel subclass + public class DerivedPageModel : MyPageModel + { + public void OnPost(string derivedParam) { } + } } diff --git a/csharp/ql/test/library-tests/dataflow/flowsources/aspremote/aspRemoteFlowSource.expected b/csharp/ql/test/library-tests/dataflow/flowsources/aspremote/aspRemoteFlowSource.expected index d729eb939d2..ef7a8f3cd78 100644 --- a/csharp/ql/test/library-tests/dataflow/flowsources/aspremote/aspRemoteFlowSource.expected +++ b/csharp/ql/test/library-tests/dataflow/flowsources/aspremote/aspRemoteFlowSource.expected @@ -14,3 +14,10 @@ remoteFlowSources | AspRemoteFlowSource.cs:54:69:54:82 | mapDeleteParam | | AspRemoteFlowSource.cs:56:41:56:44 | item | | AspRemoteFlowSource.cs:64:43:64:47 | param | +| AspRemoteFlowSource.cs:71:34:71:35 | id | +| AspRemoteFlowSource.cs:73:35:73:41 | command | +| AspRemoteFlowSource.cs:73:48:73:52 | count | +| AspRemoteFlowSource.cs:75:40:75:43 | data | +| AspRemoteFlowSource.cs:77:34:77:38 | value | +| AspRemoteFlowSource.cs:79:37:79:42 | itemId | +| AspRemoteFlowSource.cs:92:35:92:46 | derivedParam | diff --git a/csharp/ql/test/library-tests/properties/PrintAst.expected b/csharp/ql/test/library-tests/properties/PrintAst.expected index ef482ed33d0..d07cc484c71 100644 --- a/csharp/ql/test/library-tests/properties/PrintAst.expected +++ b/csharp/ql/test/library-tests/properties/PrintAst.expected @@ -293,3 +293,69 @@ properties.cs: # 160| 0: [LocalVariableAccess] access to local variable x # 160| 1: [PropertyCall] access to property Prop # 160| -1: [LocalVariableAccess] access to local variable s +# 164| 13: [Class] BaseClass +# 166| 6: [Property] Value +# 166| -1: [TypeMention] int +# 168| 3: [Getter] get_Value +# 168| 4: [BlockStmt] {...} +# 168| 0: [ReturnStmt] return ...; +# 168| 0: [FieldAccess] access to field Value.field +# 169| 4: [Setter] set_Value +#-----| 2: (Parameters) +# 169| 0: [Parameter] value +# 169| 4: [BlockStmt] {...} +# 169| 0: [ExprStmt] ...; +# 169| 0: [AssignExpr] ... = ... +# 169| 0: [FieldAccess] access to field Value.field +# 169| 1: [ParameterAccess] access to parameter value +# 166| 7: [Field] Value.field +# 173| 14: [Class] DerivedClass1 +#-----| 3: (Base types) +# 173| 0: [TypeMention] BaseClass +# 175| 6: [Property] Value +# 175| -1: [TypeMention] int +# 177| 3: [Getter] get_Value +# 177| 4: [BlockStmt] {...} +# 177| 0: [ReturnStmt] return ...; +# 177| 0: [IntLiteral] 20 +# 181| 15: [Class] DerivedClass2 +#-----| 3: (Base types) +# 181| 0: [TypeMention] BaseClass +# 183| 16: [Class] TestPartialPropertyOverride +# 185| 6: [Method] M +# 185| -1: [TypeMention] Void +# 186| 4: [BlockStmt] {...} +# 187| 0: [LocalVariableDeclStmt] ... ...; +# 187| 0: [LocalVariableDeclAndInitExpr] DerivedClass1 d1 = ... +# 187| -1: [TypeMention] DerivedClass1 +# 187| 0: [LocalVariableAccess] access to local variable d1 +# 187| 1: [ObjectCreation] object creation of type DerivedClass1 +# 187| 0: [TypeMention] DerivedClass1 +# 188| 1: [ExprStmt] ...; +# 188| 0: [AssignExpr] ... = ... +# 188| 0: [PropertyCall] access to property Value +# 188| -1: [LocalVariableAccess] access to local variable d1 +# 188| 1: [IntLiteral] 11 +# 189| 2: [LocalVariableDeclStmt] ... ...; +# 189| 0: [LocalVariableDeclAndInitExpr] Int32 test1 = ... +# 189| -1: [TypeMention] int +# 189| 0: [LocalVariableAccess] access to local variable test1 +# 189| 1: [PropertyCall] access to property Value +# 189| -1: [LocalVariableAccess] access to local variable d1 +# 191| 3: [LocalVariableDeclStmt] ... ...; +# 191| 0: [LocalVariableDeclAndInitExpr] DerivedClass2 d2 = ... +# 191| -1: [TypeMention] DerivedClass2 +# 191| 0: [LocalVariableAccess] access to local variable d2 +# 191| 1: [ObjectCreation] object creation of type DerivedClass2 +# 191| 0: [TypeMention] DerivedClass2 +# 192| 4: [ExprStmt] ...; +# 192| 0: [AssignExpr] ... = ... +# 192| 0: [PropertyCall] access to property Value +# 192| -1: [LocalVariableAccess] access to local variable d2 +# 192| 1: [IntLiteral] 12 +# 193| 5: [LocalVariableDeclStmt] ... ...; +# 193| 0: [LocalVariableDeclAndInitExpr] Int32 test2 = ... +# 193| -1: [TypeMention] int +# 193| 0: [LocalVariableAccess] access to local variable test2 +# 193| 1: [PropertyCall] access to property Value +# 193| -1: [LocalVariableAccess] access to local variable d2 diff --git a/csharp/ql/test/library-tests/properties/Properties17.expected b/csharp/ql/test/library-tests/properties/Properties17.expected index 74efae145f7..7e031d39aaf 100644 --- a/csharp/ql/test/library-tests/properties/Properties17.expected +++ b/csharp/ql/test/library-tests/properties/Properties17.expected @@ -1,4 +1,5 @@ | Prop.field | +| Value.field | | caption | | next | | x | diff --git a/csharp/ql/test/library-tests/properties/Properties19.expected b/csharp/ql/test/library-tests/properties/Properties19.expected index 7c027119067..0c2ba9c8ceb 100644 --- a/csharp/ql/test/library-tests/properties/Properties19.expected +++ b/csharp/ql/test/library-tests/properties/Properties19.expected @@ -6,3 +6,7 @@ | properties.cs:71:28:71:28 | Y | properties.cs:83:39:83:44 | access to property Y | properties.cs:74:13:74:15 | set_Y | | properties.cs:146:24:146:27 | Prop | properties.cs:159:13:159:18 | access to property Prop | properties.cs:148:13:148:15 | get_Prop | | properties.cs:146:24:146:27 | Prop | properties.cs:160:21:160:26 | access to property Prop | properties.cs:148:13:148:15 | get_Prop | +| properties.cs:166:28:166:32 | Value | properties.cs:192:13:192:20 | access to property Value | properties.cs:169:13:169:15 | set_Value | +| properties.cs:166:28:166:32 | Value | properties.cs:193:25:193:32 | access to property Value | properties.cs:168:13:168:15 | get_Value | +| properties.cs:175:29:175:33 | Value | properties.cs:188:13:188:20 | access to property Value | properties.cs:169:13:169:15 | set_Value | +| properties.cs:175:29:175:33 | Value | properties.cs:189:25:189:32 | access to property Value | properties.cs:177:13:177:15 | get_Value | diff --git a/csharp/ql/test/library-tests/properties/properties.cs b/csharp/ql/test/library-tests/properties/properties.cs index 391245e3497..f2f72638838 100644 --- a/csharp/ql/test/library-tests/properties/properties.cs +++ b/csharp/ql/test/library-tests/properties/properties.cs @@ -160,4 +160,37 @@ namespace Properties var x = s.Prop; } } + + public class BaseClass + { + public virtual int Value + { + get { return field; } + set { field = value; } + } + } + + public class DerivedClass1 : BaseClass + { + public override int Value + { + get { return 20; } + } + } + + public class DerivedClass2 : BaseClass { } + + public class TestPartialPropertyOverride + { + public void M() + { + var d1 = new DerivedClass1(); + d1.Value = 11; + var test1 = d1.Value; + + var d2 = new DerivedClass2(); + d2.Value = 12; + var test2 = d2.Value; + } + } } diff --git a/csharp/ql/test/library-tests/spans/Slice.cs b/csharp/ql/test/library-tests/spans/Slice.cs new file mode 100644 index 00000000000..67f937906ae --- /dev/null +++ b/csharp/ql/test/library-tests/spans/Slice.cs @@ -0,0 +1,29 @@ +using System; + +public class C +{ + public void M(int a, int b) + { + var s = "hello world"; + var sub1 = s[1..a]; + var sub2 = s[..2]; + var sub3 = s[3..]; + var sub4 = s[..^4]; + var sub5 = s[a..^b]; + var sub6 = s[..]; + + Range range = 1..a; + var sub7 = s[range]; + + Span sp = null; + var slice1 = sp[5..a]; + var slice2 = sp[..6]; + var slice3 = sp[7..]; + var slice4 = sp[..^8]; + var slice5 = sp[a..^b]; + var slice6 = sp[..]; + + Range range2 = 1..a; + var slice7 = sp[range2]; + } +} diff --git a/csharp/ql/test/library-tests/spans/slice.expected b/csharp/ql/test/library-tests/spans/slice.expected new file mode 100644 index 00000000000..4603dcfcac4 --- /dev/null +++ b/csharp/ql/test/library-tests/spans/slice.expected @@ -0,0 +1,41 @@ +methodArguments +| Slice.cs:8:20:8:26 | call to method Substring | Substring(int, int) | 0 | 1 | +| Slice.cs:8:20:8:26 | call to method Substring | Substring(int, int) | 1 | access to parameter a | +| Slice.cs:9:20:9:25 | call to method Substring | Substring(int, int) | 0 | 0 | +| Slice.cs:9:20:9:25 | call to method Substring | Substring(int, int) | 1 | 2 | +| Slice.cs:10:20:10:25 | call to method Substring | Substring(int, int) | 0 | 3 | +| Slice.cs:10:20:10:25 | call to method Substring | Substring(int, int) | 1 | ^0 | +| Slice.cs:11:20:11:26 | call to method Substring | Substring(int, int) | 0 | 0 | +| Slice.cs:11:20:11:26 | call to method Substring | Substring(int, int) | 1 | ^4 | +| Slice.cs:12:20:12:27 | call to method Substring | Substring(int, int) | 0 | access to parameter a | +| Slice.cs:12:20:12:27 | call to method Substring | Substring(int, int) | 1 | ^access to parameter b | +| Slice.cs:13:20:13:24 | call to method Substring | Substring(int, int) | 0 | 0 | +| Slice.cs:13:20:13:24 | call to method Substring | Substring(int, int) | 1 | ^0 | +| Slice.cs:19:22:19:29 | call to method Slice | Slice(int, int) | 0 | 5 | +| Slice.cs:19:22:19:29 | call to method Slice | Slice(int, int) | 1 | access to parameter a | +| Slice.cs:20:22:20:28 | call to method Slice | Slice(int, int) | 0 | 0 | +| Slice.cs:20:22:20:28 | call to method Slice | Slice(int, int) | 1 | 6 | +| Slice.cs:21:22:21:28 | call to method Slice | Slice(int, int) | 0 | 7 | +| Slice.cs:21:22:21:28 | call to method Slice | Slice(int, int) | 1 | ^0 | +| Slice.cs:22:22:22:29 | call to method Slice | Slice(int, int) | 0 | 0 | +| Slice.cs:22:22:22:29 | call to method Slice | Slice(int, int) | 1 | ^8 | +| Slice.cs:23:22:23:30 | call to method Slice | Slice(int, int) | 0 | access to parameter a | +| Slice.cs:23:22:23:30 | call to method Slice | Slice(int, int) | 1 | ^access to parameter b | +| Slice.cs:24:22:24:27 | call to method Slice | Slice(int, int) | 0 | 0 | +| Slice.cs:24:22:24:27 | call to method Slice | Slice(int, int) | 1 | ^0 | +methodCalls +| Slice.cs:3:14:3:14 | call to method | () | +| Slice.cs:8:20:8:26 | call to method Substring | Substring(int, int) | +| Slice.cs:9:20:9:25 | call to method Substring | Substring(int, int) | +| Slice.cs:10:20:10:25 | call to method Substring | Substring(int, int) | +| Slice.cs:11:20:11:26 | call to method Substring | Substring(int, int) | +| Slice.cs:12:20:12:27 | call to method Substring | Substring(int, int) | +| Slice.cs:13:20:13:24 | call to method Substring | Substring(int, int) | +| Slice.cs:16:20:16:27 | call to method Substring | Substring(int, int) | +| Slice.cs:19:22:19:29 | call to method Slice | Slice(int, int) | +| Slice.cs:20:22:20:28 | call to method Slice | Slice(int, int) | +| Slice.cs:21:22:21:28 | call to method Slice | Slice(int, int) | +| Slice.cs:22:22:22:29 | call to method Slice | Slice(int, int) | +| Slice.cs:23:22:23:30 | call to method Slice | Slice(int, int) | +| Slice.cs:24:22:24:27 | call to method Slice | Slice(int, int) | +| Slice.cs:27:22:27:31 | call to method Slice | Slice(int, int) | diff --git a/csharp/ql/test/library-tests/spans/slice.ql b/csharp/ql/test/library-tests/spans/slice.ql new file mode 100644 index 00000000000..f0d1ffe4549 --- /dev/null +++ b/csharp/ql/test/library-tests/spans/slice.ql @@ -0,0 +1,17 @@ +import csharp + +private string printExpr(Expr e) { + e = any(IndexExpr index | result = "^" + index.getExpr().toString()) + or + not e instanceof IndexExpr and + result = e.toString() +} + +query predicate methodArguments(MethodCall mc, string target, int i, string arg) { + target = mc.getTarget().toStringWithTypes() and + arg = printExpr(mc.getArgument(i)) +} + +query predicate methodCalls(MethodCall mc, string target) { + target = mc.getTarget().toStringWithTypes() +} diff --git a/csharp/ql/test/query-tests/Telemetry/DatabaseQuality/IsNotOkayCall.expected b/csharp/ql/test/query-tests/Telemetry/DatabaseQuality/IsNotOkayCall.expected index dcdb8b09058..e69de29bb2d 100644 --- a/csharp/ql/test/query-tests/Telemetry/DatabaseQuality/IsNotOkayCall.expected +++ b/csharp/ql/test/query-tests/Telemetry/DatabaseQuality/IsNotOkayCall.expected @@ -1,2 +0,0 @@ -| Quality.cs:26:19:26:26 | access to indexer | Call without target $@. | Quality.cs:26:19:26:26 | access to indexer | access to indexer | -| Quality.cs:29:21:29:27 | access to indexer | Call without target $@. | Quality.cs:29:21:29:27 | access to indexer | access to indexer | diff --git a/csharp/ql/test/query-tests/Telemetry/DatabaseQuality/NoTarget.expected b/csharp/ql/test/query-tests/Telemetry/DatabaseQuality/NoTarget.expected index a76dd08cdb6..b96815507f1 100644 --- a/csharp/ql/test/query-tests/Telemetry/DatabaseQuality/NoTarget.expected +++ b/csharp/ql/test/query-tests/Telemetry/DatabaseQuality/NoTarget.expected @@ -7,7 +7,5 @@ | Quality.cs:20:13:20:23 | access to property MyProperty6 | Call without target $@. | Quality.cs:20:13:20:23 | access to property MyProperty6 | access to property MyProperty6 | | Quality.cs:23:9:23:14 | access to event Event1 | Call without target $@. | Quality.cs:23:9:23:14 | access to event Event1 | access to event Event1 | | Quality.cs:23:9:23:30 | delegate call | Call without target $@. | Quality.cs:23:9:23:30 | delegate call | delegate call | -| Quality.cs:26:19:26:26 | access to indexer | Call without target $@. | Quality.cs:26:19:26:26 | access to indexer | access to indexer | -| Quality.cs:29:21:29:27 | access to indexer | Call without target $@. | Quality.cs:29:21:29:27 | access to indexer | access to indexer | | Quality.cs:38:16:38:26 | access to property MyProperty2 | Call without target $@. | Quality.cs:38:16:38:26 | access to property MyProperty2 | access to property MyProperty2 | | Quality.cs:50:20:50:26 | object creation of type T | Call without target $@. | Quality.cs:50:20:50:26 | object creation of type T | object creation of type T | diff --git a/csharp/ql/test/query-tests/Telemetry/DatabaseQuality/Quality.cs b/csharp/ql/test/query-tests/Telemetry/DatabaseQuality/Quality.cs index e10ce10f6c4..648083edad8 100644 --- a/csharp/ql/test/query-tests/Telemetry/DatabaseQuality/Quality.cs +++ b/csharp/ql/test/query-tests/Telemetry/DatabaseQuality/Quality.cs @@ -23,10 +23,10 @@ public class Test Event1.Invoke(this, 5); var str = "abcd"; - var sub = str[..3]; // TODO: this is not an indexer call, but rather a `str.Substring(0, 3)` call. + var sub = str[..3]; Span sp = null; - var slice = sp[..3]; // TODO: this is not an indexer call, but rather a `sp.Slice(0, 3)` call. + var slice = sp[..3]; Span guidBytes = stackalloc byte[16]; guidBytes[08] = 1; diff --git a/java/ql/integration-tests/java/buildless-erroneous/test.py b/java/ql/integration-tests/java/buildless-erroneous/test.py index 834b1132cf1..aa78b3574f9 100644 --- a/java/ql/integration-tests/java/buildless-erroneous/test.py +++ b/java/ql/integration-tests/java/buildless-erroneous/test.py @@ -1,2 +1,2 @@ -def test(codeql, java): +def test(codeql, java, check_diagnostics_java): codeql.database.create(_env={"CODEQL_EXTRACTOR_JAVA_OPTION_BUILDLESS": "true"}) diff --git a/java/ql/integration-tests/java/buildless-gradle-boms/test.py b/java/ql/integration-tests/java/buildless-gradle-boms/test.py index bea3e5f552c..9611010179d 100644 --- a/java/ql/integration-tests/java/buildless-gradle-boms/test.py +++ b/java/ql/integration-tests/java/buildless-gradle-boms/test.py @@ -1,4 +1,4 @@ -def test(codeql, java, gradle_8_3): +def test(codeql, java, gradle_8_3, check_diagnostics_java): codeql.database.create( _env={ "CODEQL_EXTRACTOR_JAVA_OPTION_BUILDLESS": "true", diff --git a/java/ql/integration-tests/java/buildless-gradle-classifiers/test.py b/java/ql/integration-tests/java/buildless-gradle-classifiers/test.py index bea3e5f552c..9611010179d 100644 --- a/java/ql/integration-tests/java/buildless-gradle-classifiers/test.py +++ b/java/ql/integration-tests/java/buildless-gradle-classifiers/test.py @@ -1,4 +1,4 @@ -def test(codeql, java, gradle_8_3): +def test(codeql, java, gradle_8_3, check_diagnostics_java): codeql.database.create( _env={ "CODEQL_EXTRACTOR_JAVA_OPTION_BUILDLESS": "true", diff --git a/java/ql/integration-tests/java/buildless-gradle-timeout/test.py b/java/ql/integration-tests/java/buildless-gradle-timeout/test.py index b0e307f15bb..8fcd60479d5 100644 --- a/java/ql/integration-tests/java/buildless-gradle-timeout/test.py +++ b/java/ql/integration-tests/java/buildless-gradle-timeout/test.py @@ -1,4 +1,4 @@ -def test(codeql, java): +def test(codeql, java, check_diagnostics_java): # gradlew has been rigged to stall for a long time by trying to fetch from a black-hole IP. # We should find the timeout logic fires and buildless aborts the Gradle run quickly. codeql.database.create( diff --git a/java/ql/integration-tests/java/buildless-gradle/test.py b/java/ql/integration-tests/java/buildless-gradle/test.py index bea3e5f552c..9611010179d 100644 --- a/java/ql/integration-tests/java/buildless-gradle/test.py +++ b/java/ql/integration-tests/java/buildless-gradle/test.py @@ -1,4 +1,4 @@ -def test(codeql, java, gradle_8_3): +def test(codeql, java, gradle_8_3, check_diagnostics_java): codeql.database.create( _env={ "CODEQL_EXTRACTOR_JAVA_OPTION_BUILDLESS": "true", diff --git a/java/ql/integration-tests/java/buildless-inherit-trust-store/test.py b/java/ql/integration-tests/java/buildless-inherit-trust-store/test.py index 93a527620e1..06855e43ba2 100644 --- a/java/ql/integration-tests/java/buildless-inherit-trust-store/test.py +++ b/java/ql/integration-tests/java/buildless-inherit-trust-store/test.py @@ -3,7 +3,7 @@ import os import runs_on -def test(codeql, java, cwd): +def test(codeql, java, cwd, check_diagnostics_java): # This serves the "repo" directory on https://locahost:4443 command = ["python3", "../server.py"] if runs_on.github_actions and runs_on.posix: diff --git a/java/ql/integration-tests/java/buildless-maven-executable-war/test.py b/java/ql/integration-tests/java/buildless-maven-executable-war/test.py index a92ac46584c..2a839a0c294 100644 --- a/java/ql/integration-tests/java/buildless-maven-executable-war/test.py +++ b/java/ql/integration-tests/java/buildless-maven-executable-war/test.py @@ -1,4 +1,4 @@ -def test(codeql, java): +def test(codeql, java, check_diagnostics_java): codeql.database.create( _env={ "CODEQL_EXTRACTOR_JAVA_OPTION_BUILDLESS": "true", diff --git a/java/ql/integration-tests/java/buildless-maven-existing-settings-xml/test.py b/java/ql/integration-tests/java/buildless-maven-existing-settings-xml/test.py index fc10b066d0b..811ef3bb926 100644 --- a/java/ql/integration-tests/java/buildless-maven-existing-settings-xml/test.py +++ b/java/ql/integration-tests/java/buildless-maven-existing-settings-xml/test.py @@ -1,7 +1,7 @@ import os import os.path -def test(codeql, java): +def test(codeql, java, check_diagnostics_java): codeql.database.create(build_mode = "none", _env={ "_JAVA_OPTIONS": "-Duser.home=" + os.path.join(os.getcwd(), "home-dir-with-maven-settings") diff --git a/java/ql/integration-tests/java/buildless-maven-mirrorof/test.py b/java/ql/integration-tests/java/buildless-maven-mirrorof/test.py index 9cae7b67553..c24417c1440 100644 --- a/java/ql/integration-tests/java/buildless-maven-mirrorof/test.py +++ b/java/ql/integration-tests/java/buildless-maven-mirrorof/test.py @@ -1,7 +1,7 @@ import os import os.path -def test(codeql, java): +def test(codeql, java, check_diagnostics_java): codeql.database.create(build_mode = "none", _env={ "_JAVA_OPTIONS": "-Duser.home=" + os.path.join(os.getcwd(), "empty-home"), diff --git a/java/ql/integration-tests/java/buildless-maven-multimodule/test.py b/java/ql/integration-tests/java/buildless-maven-multimodule/test.py index a92ac46584c..2a839a0c294 100644 --- a/java/ql/integration-tests/java/buildless-maven-multimodule/test.py +++ b/java/ql/integration-tests/java/buildless-maven-multimodule/test.py @@ -1,4 +1,4 @@ -def test(codeql, java): +def test(codeql, java, check_diagnostics_java): codeql.database.create( _env={ "CODEQL_EXTRACTOR_JAVA_OPTION_BUILDLESS": "true", diff --git a/java/ql/integration-tests/java/buildless-maven-timeout/test.py b/java/ql/integration-tests/java/buildless-maven-timeout/test.py index 2c70d7dd91a..7bf7e25357f 100644 --- a/java/ql/integration-tests/java/buildless-maven-timeout/test.py +++ b/java/ql/integration-tests/java/buildless-maven-timeout/test.py @@ -1,4 +1,4 @@ -def test(codeql, java): +def test(codeql, java, check_diagnostics_java): # mvnw has been rigged to stall for a long time by trying to fetch from a black-hole IP. We should find the timeout logic fires and buildless aborts the Maven run quickly. codeql.database.create( build_mode="none", diff --git a/java/ql/integration-tests/java/buildless-maven/test.py b/java/ql/integration-tests/java/buildless-maven/test.py index 958eddca2c7..2e49378d982 100644 --- a/java/ql/integration-tests/java/buildless-maven/test.py +++ b/java/ql/integration-tests/java/buildless-maven/test.py @@ -1,7 +1,7 @@ import os import os.path -def test(codeql, java): +def test(codeql, java, check_diagnostics_java): codeql.database.create(build_mode = "none", _env={ "_JAVA_OPTIONS": "-Duser.home=" + os.path.join(os.getcwd(), "empty-home") diff --git a/java/ql/integration-tests/java/buildless-proxy-gradle/test.py b/java/ql/integration-tests/java/buildless-proxy-gradle/test.py index 970c78f97ab..251efbede22 100644 --- a/java/ql/integration-tests/java/buildless-proxy-gradle/test.py +++ b/java/ql/integration-tests/java/buildless-proxy-gradle/test.py @@ -1,4 +1,4 @@ -def test(codeql, java, codeql_mitm_proxy, gradle_8_3): +def test(codeql, java, codeql_mitm_proxy, gradle_8_3, check_diagnostics_java): codeql.database.create( _env={ "CODEQL_EXTRACTOR_JAVA_OPTION_BUILDLESS": "true", diff --git a/java/ql/integration-tests/java/buildless-proxy-maven/test.py b/java/ql/integration-tests/java/buildless-proxy-maven/test.py index c8919d321fa..879a1b3a80a 100644 --- a/java/ql/integration-tests/java/buildless-proxy-maven/test.py +++ b/java/ql/integration-tests/java/buildless-proxy-maven/test.py @@ -1,4 +1,4 @@ -def test(codeql, java, codeql_mitm_proxy): +def test(codeql, java, codeql_mitm_proxy, check_diagnostics_java): codeql.database.create( _env={ "CODEQL_EXTRACTOR_JAVA_OPTION_BUILDLESS": "true", diff --git a/java/ql/integration-tests/java/buildless-sibling-projects/test.py b/java/ql/integration-tests/java/buildless-sibling-projects/test.py index 1b7cae27c64..65ae24ed441 100644 --- a/java/ql/integration-tests/java/buildless-sibling-projects/test.py +++ b/java/ql/integration-tests/java/buildless-sibling-projects/test.py @@ -1,4 +1,4 @@ -def test(codeql, use_java_11, java, actions_toolchains_file): +def test(codeql, use_java_11, java, actions_toolchains_file, check_diagnostics_java): # The version of gradle used doesn't work on java 17 codeql.database.create( _env={ diff --git a/java/ql/integration-tests/java/buildless/test.py b/java/ql/integration-tests/java/buildless/test.py index 834b1132cf1..aa78b3574f9 100644 --- a/java/ql/integration-tests/java/buildless/test.py +++ b/java/ql/integration-tests/java/buildless/test.py @@ -1,2 +1,2 @@ -def test(codeql, java): +def test(codeql, java, check_diagnostics_java): codeql.database.create(_env={"CODEQL_EXTRACTOR_JAVA_OPTION_BUILDLESS": "true"}) diff --git a/java/ql/lib/semmle/code/java/dataflow/TypeFlow.qll b/java/ql/lib/semmle/code/java/dataflow/TypeFlow.qll index 2c04a6413eb..e11013f1232 100644 --- a/java/ql/lib/semmle/code/java/dataflow/TypeFlow.qll +++ b/java/ql/lib/semmle/code/java/dataflow/TypeFlow.qll @@ -72,6 +72,35 @@ module FlowStepsInput implements UniversalFlow::UniversalFlowInput { } } + private class FlowNodeElement extends Element { + FlowNodeElement() { + this instanceof Field or + this instanceof Expr or + this instanceof Method + } + } + + private predicate id(FlowNodeElement x, FlowNodeElement y) { x = y } + + private predicate idOf(FlowNodeElement x, int y) = equivalenceRelation(id/2)(x, y) + + int getFlowNodeId(FlowNode n) { + n = + rank[result](FlowNode n0, int a, int b | + a = 0 and + idOf(n0.asField(), b) + or + // no case for `n0.asSsa()`; here we rely on the built-in location-based ranking + a = 1 and + idOf(n0.asExpr(), b) + or + a = 2 and + idOf(n0.asMethod(), b) + | + n0 order by a, b + ) + } + private SrcCallable viableCallable_v1(Call c) { result = viableImpl_v1(c) or @@ -165,6 +194,8 @@ private module Input implements TypeFlowInput { class TypeFlowNode = FlowNode; + predicate getTypeFlowNodeId = FlowStepsInput::getFlowNodeId/1; + predicate isExcludedFromNullAnalysis = FlowStepsInput::isExcludedFromNullAnalysis/1; class Type = RefType; diff --git a/java/ql/lib/semmle/code/java/security/ListOfConstantsSanitizer.qll b/java/ql/lib/semmle/code/java/security/ListOfConstantsSanitizer.qll index c8d52f4191c..b00c5c99405 100644 --- a/java/ql/lib/semmle/code/java/security/ListOfConstantsSanitizer.qll +++ b/java/ql/lib/semmle/code/java/security/ListOfConstantsSanitizer.qll @@ -170,6 +170,8 @@ private class EmptyCollectionConstructor extends Constructor { private module CollectionFlowStepsInput implements UniversalFlow::UniversalFlowInput { import FlowStepsInput + predicate getFlowNodeId = FlowStepsInput::getFlowNodeId/1; + /** * Holds if `n2` is a collection/array/constant whose value(s) are * determined completely from the range of `n1` nodes. diff --git a/java/ql/test/query-tests/Nullness/B.java b/java/ql/test/query-tests/Nullness/B.java index 14ff2b35935..bc8b0bac154 100644 --- a/java/ql/test/query-tests/Nullness/B.java +++ b/java/ql/test/query-tests/Nullness/B.java @@ -331,7 +331,7 @@ public class B { x = new Object(); } if(y instanceof String) { - x.hashCode(); // $ Alert[java/dereferenced-value-may-be-null] // Spurious NPE - false positive + x.hashCode(); // $ SPURIOUS: Alert[java/dereferenced-value-may-be-null] // Spurious NPE - false positive } } @@ -341,7 +341,7 @@ public class B { x = new Object(); } if(!(y instanceof String)) { - x.hashCode(); // $ Alert[java/dereferenced-value-may-be-null] // Spurious NPE - false positive + x.hashCode(); // $ SPURIOUS: Alert[java/dereferenced-value-may-be-null] // Spurious NPE - false positive } } @@ -351,7 +351,7 @@ public class B { x = new Object(); } if(y == z) { - x.hashCode(); // $ Alert[java/dereferenced-value-may-be-null] // Spurious NPE - false positive + x.hashCode(); // $ SPURIOUS: Alert[java/dereferenced-value-may-be-null] // Spurious NPE - false positive } Object x2 = null; @@ -359,7 +359,7 @@ public class B { x2 = new Object(); } if(y != z) { - x2.hashCode(); // $ Alert[java/dereferenced-value-may-be-null] // Spurious NPE - false positive + x2.hashCode(); // $ SPURIOUS: Alert[java/dereferenced-value-may-be-null] // Spurious NPE - false positive } Object x3 = null; @@ -367,7 +367,7 @@ public class B { x3 = new Object(); } if(!(y == z)) { - x3.hashCode(); // $ Alert[java/dereferenced-value-may-be-null] // Spurious NPE - false positive + x3.hashCode(); // $ SPURIOUS: Alert[java/dereferenced-value-may-be-null] // Spurious NPE - false positive } } @@ -462,7 +462,7 @@ public class B { cur = a[i]; if (!prev) { // correctly guarded by !cur from the _previous_ iteration - x.hashCode(); // $ Alert[java/dereferenced-value-may-be-null] // Spurious NPE - false positive + x.hashCode(); // $ SPURIOUS: Alert[java/dereferenced-value-may-be-null] // Spurious NPE - false positive } else { x = new Object(); } @@ -484,7 +484,7 @@ public class B { t = new Object(); } // correctly guarded by t: null -> String -> Object - x.hashCode(); // $ Alert[java/dereferenced-value-may-be-null] // Spurious NPE - false positive + x.hashCode(); // $ SPURIOUS: Alert[java/dereferenced-value-may-be-null] // Spurious NPE - false positive } } } @@ -573,7 +573,7 @@ public class B { } finally { } } - s.hashCode(); // $ Alert[java/dereferenced-value-may-be-null] // Spurious NPE - false positive + s.hashCode(); // $ SPURIOUS: Alert[java/dereferenced-value-may-be-null] // Spurious NPE - false positive // CFG reachability does not distinguish abrupt successors } } diff --git a/java/ql/test/query-tests/Nullness/C.java b/java/ql/test/query-tests/Nullness/C.java index 0ecc0c23f88..bbe2eb597b2 100644 --- a/java/ql/test/query-tests/Nullness/C.java +++ b/java/ql/test/query-tests/Nullness/C.java @@ -6,8 +6,8 @@ public class C { long[][] a2 = null; boolean haveA2 = ix < len && (a2 = a1[ix]) != null; long[] a3 = null; - final boolean haveA3 = haveA2 && (a3 = a2[ix]) != null; // $ Alert[java/dereferenced-value-may-be-null] // NPE - false positive - if (haveA3) a3[0] = 0; // $ Alert[java/dereferenced-value-may-be-null] // NPE - false positive + final boolean haveA3 = haveA2 && (a3 = a2[ix]) != null; // $ SPURIOUS: Alert[java/dereferenced-value-may-be-null] // NPE - false positive + if (haveA3) a3[0] = 0; // $ SPURIOUS: Alert[java/dereferenced-value-may-be-null] // NPE - false positive } public void ex2(boolean x, boolean y) { @@ -18,7 +18,7 @@ public class C { s2 = (s1 == null) ? null : ""; } if (s2 != null) - s1.hashCode(); // $ Alert[java/dereferenced-value-may-be-null] // NPE - false positive + s1.hashCode(); // $ SPURIOUS: Alert[java/dereferenced-value-may-be-null] // NPE - false positive } public void ex3(List ss) { @@ -48,7 +48,7 @@ public class C { slice = new ArrayList<>(); result.add(slice); } - slice.add(str); // $ Alert[java/dereferenced-value-may-be-null] // NPE - false positive + slice.add(str); // $ SPURIOUS: Alert[java/dereferenced-value-may-be-null] // NPE - false positive ++index; iter.remove(); } @@ -141,7 +141,7 @@ public class C { public void ex10(int[] a) { int n = a == null ? 0 : a.length; for (int i = 0; i < n; i++) { - int x = a[i]; // $ Alert[java/dereferenced-value-may-be-null] // NPE - false positive + int x = a[i]; // $ SPURIOUS: Alert[java/dereferenced-value-may-be-null] // NPE - false positive if (x > 7) a = new int[n]; } @@ -216,7 +216,7 @@ public class C { if (o1 == o2) { return; } - if (o1.equals(o2)) { // $ Alert[java/dereferenced-value-may-be-null] // NPE - false positive + if (o1.equals(o2)) { // $ SPURIOUS: Alert[java/dereferenced-value-may-be-null] // NPE - false positive return; } } @@ -230,7 +230,7 @@ public class C { public static void ex16(C c) { int[] xs = c.getFoo16() != null ? new int[5] : null; if (c.getFoo16() != null) { - xs[0]++; // $ Alert[java/dereferenced-value-may-be-null] // NPE - false positive + xs[0]++; // $ SPURIOUS: Alert[java/dereferenced-value-may-be-null] // NPE - false positive } } diff --git a/java/ql/test/query-tests/UseBraces/UseBraces.java b/java/ql/test/query-tests/UseBraces/UseBraces.java index 0177d68571e..1e5487f1f7b 100644 --- a/java/ql/test/query-tests/UseBraces/UseBraces.java +++ b/java/ql/test/query-tests/UseBraces/UseBraces.java @@ -11,25 +11,25 @@ class UseBraces { int x = 0, y; int[] branches = new int[10]; - + // If-then statement - + if(1==1) { f(); } g(); // No alert - - if(1==1) + + if(1==1) f(); g(); // No alert - + if(1==1) f(); // $ Alert - g(); // Alert - + g(); + if(1==1) - f(); g(); // $ Alert // Alert + f(); g(); // $ Alert // If-then-else statement @@ -41,29 +41,29 @@ class UseBraces { g(); } - + g(); // No alert - + if(1==2) f(); else g(); f(); // No alert - + if(true) { f(); } else f(); // $ Alert - g(); // Alert - + g(); + if(true) { f(); } else - f(); g(); // $ Alert // Alert + f(); g(); // $ Alert // While statement @@ -80,44 +80,44 @@ class UseBraces while(bb ) f(); // $ Alert - g(); // Alert + g(); g(); // No alert while(bb ) - f(); g(); // $ Alert // Alert + f(); g(); // $ Alert while(bb) if (x != 0) x = 1; // Do-while statement - + do f(); while(false); g(); // No alert - + // For statement for(int i=0; i<10; ++i) { f(); } g(); - + for(int i=0; i<10; ++i) f(); g(); - + for(int i=0; i<10; ++i) f(); // $ Alert - g(); // Alert + g(); for(int i=0; i<10; ++i) - f(); g(); // $ Alert // Alert + f(); g(); // $ Alert + - // Foreach statement - + for( int b : branches) x += b; f(); @@ -130,42 +130,42 @@ class UseBraces for( int b : branches) f(); // $ Alert - g(); // Alert + g(); for( int b : branches) - f(); g(); // $ Alert // Alert + f(); g(); // $ Alert // Nested ifs if( true ) if(false) f(); g(); // No alert - + if( true ) if(false) // $ Alert f(); - g(); // Alert - + g(); + if( true ) ; - else + else if (false) f(); g(); // No alert if( true ) ; - else + else if (false) f(); - g(); // false negative + g(); // $ MISSING: Alert // false negative if( true ) ; else if (false) f(); // $ Alert - g(); // Alert - + g(); + // Nested combinations if (true) while (x<10) @@ -175,7 +175,7 @@ class UseBraces if (true) while (x<10) // $ Alert f(); - g(); // Alert + g(); while (x<10) if (true) @@ -185,7 +185,7 @@ class UseBraces while (x<10) if (true) // $ Alert f(); - g(); // Alert + g(); if (true) f(); diff --git a/java/ql/test/query-tests/security/CWE-113/semmle/tests/ResponseSplitting.java b/java/ql/test/query-tests/security/CWE-113/semmle/tests/ResponseSplitting.java index abe71180838..7162c1c3a4d 100644 --- a/java/ql/test/query-tests/security/CWE-113/semmle/tests/ResponseSplitting.java +++ b/java/ql/test/query-tests/security/CWE-113/semmle/tests/ResponseSplitting.java @@ -62,10 +62,10 @@ public class ResponseSplitting extends HttpServlet { response.setHeader("h", t.replace('\n', ' ').replace('\r', ' ')); // FALSE NEGATIVE: replace only some line breaks - response.setHeader("h", t.replace('\n', ' ')); + response.setHeader("h", t.replace('\n', ' ')); // $ MISSING: Alert // FALSE NEGATIVE: replace only some line breaks - response.setHeader("h", t.replaceAll("\r", "")); + response.setHeader("h", t.replaceAll("\r", "")); // $ MISSING: Alert // GOOD: replace all linebreaks with a simple regex response.setHeader("h", t.replaceAll("\n", "").replaceAll("\r", "")); diff --git a/java/ql/test/query-tests/security/CWE-190/semmle/tests/ArithmeticTainted.java b/java/ql/test/query-tests/security/CWE-190/semmle/tests/ArithmeticTainted.java index 0167af87497..4c47046d3de 100644 --- a/java/ql/test/query-tests/security/CWE-190/semmle/tests/ArithmeticTainted.java +++ b/java/ql/test/query-tests/security/CWE-190/semmle/tests/ArithmeticTainted.java @@ -78,7 +78,7 @@ public class ArithmeticTainted { // FALSE NEGATIVE: stillTainted could still be very large, even // after // it has had arithmetic done on it - int output = stillTainted + 100; + int output = stillTainted + 100; // $ MISSING: Alert[java/tainted-arithmetic] } } @@ -107,7 +107,7 @@ public class ArithmeticTainted { } int output = data + 1; } - + { double x= Double.MAX_VALUE; // OK: CWE-190 only pertains to integer arithmetic diff --git a/java/ql/test/query-tests/security/CWE-190/semmle/tests/Test.java b/java/ql/test/query-tests/security/CWE-190/semmle/tests/Test.java index ed1cf0bbe1f..d274f538754 100644 --- a/java/ql/test/query-tests/security/CWE-190/semmle/tests/Test.java +++ b/java/ql/test/query-tests/security/CWE-190/semmle/tests/Test.java @@ -84,7 +84,7 @@ class Test { // FALSE POSITIVE: the query check purely based on the type, it // can't try to // determine whether the value may in fact always be in bounds - i += j; // $ Alert[java/implicit-cast-in-compound-assignment] + i += j; // $ SPURIOUS: Alert[java/implicit-cast-in-compound-assignment] } // ArithmeticWithExtremeValues @@ -224,7 +224,7 @@ class Test { // FALSE NEGATIVE: stillLarge could still be very large, even // after // it has had arithmetic done on it - int output = stillLarge + 100; + int output = stillLarge + 100; // $ MISSING: Alert[java/uncontrolled-arithmetic] } } @@ -263,7 +263,7 @@ class Test { // FALSE NEGATIVE: stillLarge could still be very large, even // after // it has had arithmetic done on it - int output = stillLarge + 100; + int output = stillLarge + 100; // $ MISSING: Alert[java/uncontrolled-arithmetic] } } diff --git a/java/ql/test/query-tests/security/CWE-311/CWE-319/HttpsUrlsTest.java b/java/ql/test/query-tests/security/CWE-311/CWE-319/HttpsUrlsTest.java index 362361a7171..4db4abe8c50 100644 --- a/java/ql/test/query-tests/security/CWE-311/CWE-319/HttpsUrlsTest.java +++ b/java/ql/test/query-tests/security/CWE-311/CWE-319/HttpsUrlsTest.java @@ -17,7 +17,7 @@ interface Hello extends java.rmi.Remote { class HelloImpl implements Hello { public static void main(String[] args) { - try { + try { // HttpsUrls { String protocol = "http://"; // $ Source[java/non-https-url] @@ -31,7 +31,7 @@ class HelloImpl implements Hello { OutputStream os = hu.getOutputStream(); hu.disconnect(); } - + { String protocol = "http"; // $ Source[java/non-https-url] URL u = new URL(protocol, "www.secret.example.org", "foo"); @@ -44,7 +44,7 @@ class HelloImpl implements Hello { OutputStream os = hu.getOutputStream(); hu.disconnect(); } - + { String protocol = "http://"; // $ Source[java/non-https-url] // the second URL overwrites the first, as it has a protocol @@ -58,7 +58,7 @@ class HelloImpl implements Hello { OutputStream os = hu.getOutputStream(); hu.disconnect(); } - + { String protocol = "https://"; URL u = new URL(protocol + "www.secret.example.org/"); @@ -70,7 +70,7 @@ class HelloImpl implements Hello { OutputStream os = hu.getOutputStream(); hu.disconnect(); } - + { String protocol = "https"; URL u = new URL(protocol, "www.secret.example.org", "foo"); @@ -82,27 +82,27 @@ class HelloImpl implements Hello { OutputStream os = hu.getOutputStream(); hu.disconnect(); } - + { - String protocol = "http"; // $ Source[java/non-https-url] + String protocol = "http"; // $ SPURIOUS: Source[java/non-https-url] URL u = new URL(protocol, "internal-url", "foo"); // FALSE POSITIVE: the query has no way of knowing whether the url will // resolve to somewhere outside the internal network, where there // are unlikely to be interception attempts - HttpsURLConnection hu = (HttpsURLConnection) u.openConnection(); // $ Alert[java/non-https-url] + HttpsURLConnection hu = (HttpsURLConnection) u.openConnection(); // $ SPURIOUS: Alert[java/non-https-url] hu.setRequestMethod("PUT"); hu.connect(); OutputStream os = hu.getOutputStream(); hu.disconnect(); } - + { String input = "URL is: http://www.secret-example.org"; String url = input.substring(8); URL u = new URL(url); // FALSE NEGATIVE: we cannot tell that the substring results in a url // string - HttpsURLConnection hu = (HttpsURLConnection) u.openConnection(); + HttpsURLConnection hu = (HttpsURLConnection) u.openConnection(); // $ MISSING: Alert[java/non-https-url] hu.setRequestMethod("PUT"); hu.connect(); OutputStream os = hu.getOutputStream(); diff --git a/python/ql/lib/CHANGELOG.md b/python/ql/lib/CHANGELOG.md index 3efb4e57482..99e46d2808a 100644 --- a/python/ql/lib/CHANGELOG.md +++ b/python/ql/lib/CHANGELOG.md @@ -2,7 +2,7 @@ ### Minor Analysis Improvements -* The sensitive data heuristics used to identify code that handles passwords and private data have been improved. Most of the changes permit more variations of established patterns, thereby finding more sensitive data. Queries that use the sensitive data library (for example `py/clear-text-logging-sensitive-data`) may find more correct results and less fewer positive results after these changes. +* The sensitive data heuristics used to identify code that handles passwords and private data have been improved. Most of the changes permit more variations of established patterns, thereby finding more sensitive data. Queries that use the sensitive data library (for example `py/clear-text-logging-sensitive-data`) may find more correct results and fewer false positive results after these changes. ## 7.1.1 diff --git a/python/ql/lib/change-notes/released/7.1.2.md b/python/ql/lib/change-notes/released/7.1.2.md index 523a14edfbe..3be115b9a93 100644 --- a/python/ql/lib/change-notes/released/7.1.2.md +++ b/python/ql/lib/change-notes/released/7.1.2.md @@ -2,4 +2,4 @@ ### Minor Analysis Improvements -* The sensitive data heuristics used to identify code that handles passwords and private data have been improved. Most of the changes permit more variations of established patterns, thereby finding more sensitive data. Queries that use the sensitive data library (for example `py/clear-text-logging-sensitive-data`) may find more correct results and less fewer positive results after these changes. +* The sensitive data heuristics used to identify code that handles passwords and private data have been improved. Most of the changes permit more variations of established patterns, thereby finding more sensitive data. Queries that use the sensitive data library (for example `py/clear-text-logging-sensitive-data`) may find more correct results and fewer false positive results after these changes. diff --git a/ql/ql/test/queries/bugs/OrderByConst/Foo.qll b/ql/ql/test/queries/bugs/OrderByConst/Foo.qll index 7229564660e..9f51572689c 100644 --- a/ql/ql/test/queries/bugs/OrderByConst/Foo.qll +++ b/ql/ql/test/queries/bugs/OrderByConst/Foo.qll @@ -1,5 +1,5 @@ string foo() { - result = concat(string x | x = [0 .. 10].toString() | x order by x desc, ", ") // BAD + result = concat(string x | x = [0 .. 10].toString() | x order by x desc, ", ") // $ Alert // BAD or result = concat(string x | x = [0 .. 10].toString() | x, ", " order by x desc) // GOOD } diff --git a/ql/ql/test/queries/bugs/OrderByConst/OrderByConst.qlref b/ql/ql/test/queries/bugs/OrderByConst/OrderByConst.qlref index 809589a856f..9c2263fc14d 100644 --- a/ql/ql/test/queries/bugs/OrderByConst/OrderByConst.qlref +++ b/ql/ql/test/queries/bugs/OrderByConst/OrderByConst.qlref @@ -1 +1,2 @@ -queries/bugs/OrderByConst.ql \ No newline at end of file +query: queries/bugs/OrderByConst.ql +postprocess: utils/test/InlineExpectationsTestQuery.ql diff --git a/ql/ql/test/queries/bugs/SumWithoutDomain/SumWithoutDomain.qlref b/ql/ql/test/queries/bugs/SumWithoutDomain/SumWithoutDomain.qlref index dc782dfbd0a..46f2785806e 100644 --- a/ql/ql/test/queries/bugs/SumWithoutDomain/SumWithoutDomain.qlref +++ b/ql/ql/test/queries/bugs/SumWithoutDomain/SumWithoutDomain.qlref @@ -1 +1,2 @@ -queries/bugs/SumWithoutDomain.ql \ No newline at end of file +query: queries/bugs/SumWithoutDomain.ql +postprocess: utils/test/InlineExpectationsTestQuery.ql diff --git a/ql/ql/test/queries/bugs/SumWithoutDomain/Test.qll b/ql/ql/test/queries/bugs/SumWithoutDomain/Test.qll index 8190aed8101..9b15c38d9c6 100644 --- a/ql/ql/test/queries/bugs/SumWithoutDomain/Test.qll +++ b/ql/ql/test/queries/bugs/SumWithoutDomain/Test.qll @@ -1,6 +1,6 @@ // Result is 3 and not 4 int foo() { - result = sum([1, 1, 2]) // <- Alert here + result = sum([1, 1, 2]) // $ Alert // <- Alert here } // Ok - false negative diff --git a/ql/ql/test/queries/overlay/InlineOverlayCaller/InlineOverlayCaller.qlref b/ql/ql/test/queries/overlay/InlineOverlayCaller/InlineOverlayCaller.qlref index 0347e9eedc5..b3385b46971 100644 --- a/ql/ql/test/queries/overlay/InlineOverlayCaller/InlineOverlayCaller.qlref +++ b/ql/ql/test/queries/overlay/InlineOverlayCaller/InlineOverlayCaller.qlref @@ -1 +1,2 @@ -queries/overlay/InlineOverlayCaller.ql +query: queries/overlay/InlineOverlayCaller.ql +postprocess: utils/test/InlineExpectationsTestQuery.ql diff --git a/ql/ql/test/queries/overlay/InlineOverlayCaller/Test.qll b/ql/ql/test/queries/overlay/InlineOverlayCaller/Test.qll index e25577d91a1..a3e2f19447a 100644 --- a/ql/ql/test/queries/overlay/InlineOverlayCaller/Test.qll +++ b/ql/ql/test/queries/overlay/InlineOverlayCaller/Test.qll @@ -4,7 +4,7 @@ module; import ql pragma[inline] -predicate foo(int x) { x = 42 } +predicate foo(int x) { x = 42 } // $ Alert overlay[caller] pragma[inline] diff --git a/ql/ql/test/queries/performance/AbstractClassImport/AbstractClassImport.qlref b/ql/ql/test/queries/performance/AbstractClassImport/AbstractClassImport.qlref index 4d7907c36ef..4dc5cc5d490 100644 --- a/ql/ql/test/queries/performance/AbstractClassImport/AbstractClassImport.qlref +++ b/ql/ql/test/queries/performance/AbstractClassImport/AbstractClassImport.qlref @@ -1 +1,2 @@ -queries/performance/AbstractClassImport.ql \ No newline at end of file +query: queries/performance/AbstractClassImport.ql +postprocess: utils/test/InlineExpectationsTestQuery.ql diff --git a/ql/ql/test/queries/performance/AbstractClassImport/AbstractClassImportTest1.qll b/ql/ql/test/queries/performance/AbstractClassImport/AbstractClassImportTest1.qll index ce7f7c4ea68..fe2519cc0d5 100644 --- a/ql/ql/test/queries/performance/AbstractClassImport/AbstractClassImportTest1.qll +++ b/ql/ql/test/queries/performance/AbstractClassImport/AbstractClassImportTest1.qll @@ -1,4 +1,4 @@ import ql import AbstractClassImportTest2 -abstract class Base extends AstNode { } +abstract class Base extends AstNode { } // $ Alert diff --git a/ql/ql/test/queries/performance/MissingNoInline/MissingNoInline.qlref b/ql/ql/test/queries/performance/MissingNoInline/MissingNoInline.qlref index aee3346d730..f1bc931e122 100644 --- a/ql/ql/test/queries/performance/MissingNoInline/MissingNoInline.qlref +++ b/ql/ql/test/queries/performance/MissingNoInline/MissingNoInline.qlref @@ -1 +1,2 @@ -queries/performance/MissingNoinline.ql \ No newline at end of file +query: queries/performance/MissingNoinline.ql +postprocess: utils/test/InlineExpectationsTestQuery.ql diff --git a/ql/ql/test/queries/performance/MissingNoInline/Test.qll b/ql/ql/test/queries/performance/MissingNoInline/Test.qll index a55315be7e2..a92f7f38d0c 100644 --- a/ql/ql/test/queries/performance/MissingNoInline/Test.qll +++ b/ql/ql/test/queries/performance/MissingNoInline/Test.qll @@ -5,7 +5,7 @@ import ql * * This predicate exists to fix a join order. */ -predicate missingNoInline(AddExpr add, Expr e1, Expr e2) { +predicate missingNoInline(AddExpr add, Expr e1, Expr e2) { // $ Alert // BAD add.getLeftOperand() = e1 and add.getRightOperand() = e2 diff --git a/ql/ql/test/queries/performance/VarUnusedInDisjunct/Test.qll b/ql/ql/test/queries/performance/VarUnusedInDisjunct/Test.qll index 10e97e58209..b4b30f10028 100644 --- a/ql/ql/test/queries/performance/VarUnusedInDisjunct/Test.qll +++ b/ql/ql/test/queries/performance/VarUnusedInDisjunct/Test.qll @@ -13,21 +13,21 @@ class MyStr extends string { predicate bad1(Big b) { b.toString().matches("%foo") or - any() + any() // $ Alert } int bad2() { exists(Big big, Small small | result = big.toString().toInt() or - result = small.toString().toInt() + result = small.toString().toInt() // $ Alert ) } float bad3(Big t) { result = [1 .. 10].toString().toFloat() or result = [11 .. 20].toString().toFloat() or - result = t.toString().toFloat() or + result = t.toString().toFloat() or // $ Alert result = [21 .. 30].toString().toFloat() } @@ -50,7 +50,7 @@ predicate bad4(Big fromType, Big toType) { or fromType.toString().matches("%foo") or - helper(toType, fromType) + helper(toType, fromType) // $ Alert } predicate good2(Big t) { @@ -71,7 +71,7 @@ predicate mixed1(Big good, Small small) { small.toString().matches("%foo") and // the use of good is fine, the comparison further up binds it. // the same is not true for bad. - (bad.toString().matches("%foo") or good.toString().regexpMatch("foo.*")) and + (bad.toString().matches("%foo") or good.toString().regexpMatch("foo.*")) and // $ Alert small.toString().regexpMatch(".*foo") ) } @@ -112,7 +112,7 @@ predicate good5(Big bb, Big v, boolean certain) { ) } -predicate bad5(Big bb) { if none() then bb.toString().matches("%foo") else any() } +predicate bad5(Big bb) { if none() then bb.toString().matches("%foo") else any() } // $ Alert pragma[inline] predicate good5(Big a, Big b) { @@ -126,12 +126,12 @@ predicate bad6(Big a) { ( a.toString().matches("%foo") // bad or - any() + any() // $ Alert ) and ( a.toString().matches("%foo") // also bad or - any() + any() // $ Alert ) } @@ -163,7 +163,7 @@ class HasField extends Big { HasField() { field = this or - this.toString().matches("%foo") // <- field only defined here. + this.toString().matches("%foo") // $ Alert // <- field only defined here. } Big getField() { result = field } diff --git a/ql/ql/test/queries/performance/VarUnusedInDisjunct/VarUnusedInDisjunct.qlref b/ql/ql/test/queries/performance/VarUnusedInDisjunct/VarUnusedInDisjunct.qlref index 28f0c0d938a..0413e31942f 100644 --- a/ql/ql/test/queries/performance/VarUnusedInDisjunct/VarUnusedInDisjunct.qlref +++ b/ql/ql/test/queries/performance/VarUnusedInDisjunct/VarUnusedInDisjunct.qlref @@ -1 +1,2 @@ -queries/performance/VarUnusedInDisjunct.ql \ No newline at end of file +query: queries/performance/VarUnusedInDisjunct.ql +postprocess: utils/test/InlineExpectationsTestQuery.ql diff --git a/ql/ql/test/queries/style/AcronymsShouldBeCamelCase/AcronymsShouldBeCamelCase.qlref b/ql/ql/test/queries/style/AcronymsShouldBeCamelCase/AcronymsShouldBeCamelCase.qlref index 0f57f1fa66c..3e287c27a39 100644 --- a/ql/ql/test/queries/style/AcronymsShouldBeCamelCase/AcronymsShouldBeCamelCase.qlref +++ b/ql/ql/test/queries/style/AcronymsShouldBeCamelCase/AcronymsShouldBeCamelCase.qlref @@ -1 +1,2 @@ -queries/style/AcronymsShouldBeCamelCase.ql \ No newline at end of file +query: queries/style/AcronymsShouldBeCamelCase.ql +postprocess: utils/test/InlineExpectationsTestQuery.ql diff --git a/ql/ql/test/queries/style/AcronymsShouldBeCamelCase/Test.qll b/ql/ql/test/queries/style/AcronymsShouldBeCamelCase/Test.qll index 1ff0d4c0d52..06742e06948 100644 --- a/ql/ql/test/queries/style/AcronymsShouldBeCamelCase/Test.qll +++ b/ql/ql/test/queries/style/AcronymsShouldBeCamelCase/Test.qll @@ -1,13 +1,13 @@ // BAD -predicate isXML() { any() } +predicate isXML() { any() } // $ Alert // GOOD [ AES is exceptional ] predicate isAES() { any() } // BAD -newtype TXMLElements = +newtype TXMLElements = // $ Alert TXmlElement() or // GOOD - TXMLElement() // BAD + TXMLElement() // $ Alert // BAD // GOOD newtype TIRFunction = MkIRFunction() diff --git a/ql/ql/test/queries/style/CouldBeCast/CouldBeCast.qlref b/ql/ql/test/queries/style/CouldBeCast/CouldBeCast.qlref index 78879bb0ab0..36a6244669b 100644 --- a/ql/ql/test/queries/style/CouldBeCast/CouldBeCast.qlref +++ b/ql/ql/test/queries/style/CouldBeCast/CouldBeCast.qlref @@ -1 +1,2 @@ -queries/style/CouldBeCast.ql \ No newline at end of file +query: queries/style/CouldBeCast.ql +postprocess: utils/test/InlineExpectationsTestQuery.ql diff --git a/ql/ql/test/queries/style/CouldBeCast/Foo.qll b/ql/ql/test/queries/style/CouldBeCast/Foo.qll index 5f6771f0043..6c3da185fe6 100644 --- a/ql/ql/test/queries/style/CouldBeCast/Foo.qll +++ b/ql/ql/test/queries/style/CouldBeCast/Foo.qll @@ -1,20 +1,20 @@ bindingset[i] predicate foo(int i) { - exists(Even j | j = i) // NOT OK + exists(Even j | j = i) // $ Alert // NOT OK or exists(Even j | j = i | j % 4 = 0) // OK or - any(Even j | j = i) = 2 // NOT OK + any(Even j | j = i) = 2 // $ Alert // NOT OK or - any(Even j | j = i | j) = 2 // NOT OK + any(Even j | j = i | j) = 2 // $ Alert // NOT OK or any(Even j | j = i | j * 2) = 4 // OK or any(Even j | j = i and j % 4 = 0 | j) = 4 // OK or - any(int j | j = i) = 2 // NOT OK + any(int j | j = i) = 2 // $ Alert // NOT OK or - exists(int j | j = i) // NOT OK + exists(int j | j = i) // $ Alert // NOT OK } class Even extends int { diff --git a/ql/ql/test/queries/style/DataFlowConfigModuleNaming/DataFlowConfigModuleNaming.qlref b/ql/ql/test/queries/style/DataFlowConfigModuleNaming/DataFlowConfigModuleNaming.qlref index 62375818f5e..2025f1cdb90 100644 --- a/ql/ql/test/queries/style/DataFlowConfigModuleNaming/DataFlowConfigModuleNaming.qlref +++ b/ql/ql/test/queries/style/DataFlowConfigModuleNaming/DataFlowConfigModuleNaming.qlref @@ -1 +1,2 @@ -queries/style/DataFlowConfigModuleNaming.ql \ No newline at end of file +query: queries/style/DataFlowConfigModuleNaming.ql +postprocess: utils/test/InlineExpectationsTestQuery.ql diff --git a/ql/ql/test/queries/style/DataFlowConfigModuleNaming/Test.qll b/ql/ql/test/queries/style/DataFlowConfigModuleNaming/Test.qll index a06118a7fe0..6da96a4b572 100644 --- a/ql/ql/test/queries/style/DataFlowConfigModuleNaming/Test.qll +++ b/ql/ql/test/queries/style/DataFlowConfigModuleNaming/Test.qll @@ -8,14 +8,14 @@ module EmptyConfig implements DataFlow::ConfigSig { } // BAD - does not end with "Config" -module EmptyConfiguration implements DataFlow::ConfigSig { +module EmptyConfiguration implements DataFlow::ConfigSig { // $ Alert predicate isSource(DataFlow::Node src) { none() } predicate isSink(DataFlow::Node sink) { none() } } // BAD - does not end with "Config" -module EmptyFlow implements DataFlow::ConfigSig { +module EmptyFlow implements DataFlow::ConfigSig { // $ Alert predicate isSource(DataFlow::Node src) { none() } predicate isSink(DataFlow::Node sink) { none() } diff --git a/ql/ql/test/queries/style/DeadCode/DeadCode.qlref b/ql/ql/test/queries/style/DeadCode/DeadCode.qlref index ac615af4961..704cc5c1365 100644 --- a/ql/ql/test/queries/style/DeadCode/DeadCode.qlref +++ b/ql/ql/test/queries/style/DeadCode/DeadCode.qlref @@ -1 +1,2 @@ -queries/style/DeadCode.ql \ No newline at end of file +query: queries/style/DeadCode.ql +postprocess: utils/test/InlineExpectationsTestQuery.ql diff --git a/ql/ql/test/queries/style/DeadCode/Foo.qll b/ql/ql/test/queries/style/DeadCode/Foo.qll index a5b5b08e2a4..32fab335b78 100644 --- a/ql/ql/test/queries/style/DeadCode/Foo.qll +++ b/ql/ql/test/queries/style/DeadCode/Foo.qll @@ -1,11 +1,11 @@ import ql private module Mixed { - private predicate dead1() { none() } + private predicate dead1() { none() } // $ Alert predicate alive1() { none() } - predicate dead2() { none() } + predicate dead2() { none() } // $ Alert } predicate usesAlive() { Mixed::alive1() } @@ -43,7 +43,7 @@ private module Input1 implements InputSig { predicate foo() { any() } } -private module Input2 implements InputSig { +private module Input2 implements InputSig { // $ Alert predicate foo() { any() } } @@ -53,7 +53,7 @@ private module Input3 implements InputSig { module M1 = ParameterizedModule; -private module M2 = ParameterizedModule; +private module M2 = ParameterizedModule; // $ Alert import ParameterizedModule @@ -65,7 +65,7 @@ private class CImpl1 extends AstNode { } final class CPublic1 = CImpl1; -private class CImpl2 extends AstNode { } +private class CImpl2 extends AstNode { } // $ Alert overlay[discard_entity] private predicate discard(@foo x) { any() } diff --git a/ql/ql/test/queries/style/FieldOnlyUsedInCharPred/FieldOnlyUsedInCharPred.qll b/ql/ql/test/queries/style/FieldOnlyUsedInCharPred/FieldOnlyUsedInCharPred.qll index edfc8b4576e..4f1d5da7196 100644 --- a/ql/ql/test/queries/style/FieldOnlyUsedInCharPred/FieldOnlyUsedInCharPred.qll +++ b/ql/ql/test/queries/style/FieldOnlyUsedInCharPred/FieldOnlyUsedInCharPred.qll @@ -1,5 +1,5 @@ class C1 extends int { - int field; // BAD + int field; // $ Alert // BAD C1() { this = field and diff --git a/ql/ql/test/queries/style/FieldOnlyUsedInCharPred/FieldOnlyUsedInCharPred.qlref b/ql/ql/test/queries/style/FieldOnlyUsedInCharPred/FieldOnlyUsedInCharPred.qlref index 0e77c6ae6fe..cf83276fb00 100644 --- a/ql/ql/test/queries/style/FieldOnlyUsedInCharPred/FieldOnlyUsedInCharPred.qlref +++ b/ql/ql/test/queries/style/FieldOnlyUsedInCharPred/FieldOnlyUsedInCharPred.qlref @@ -1 +1,2 @@ -queries/style/FieldOnlyUsedInCharPred.ql +query: queries/style/FieldOnlyUsedInCharPred.ql +postprocess: utils/test/InlineExpectationsTestQuery.ql diff --git a/ql/ql/test/queries/style/ImplicitThis/Bad.qll b/ql/ql/test/queries/style/ImplicitThis/Bad.qll index 97b51284acc..c1834c8bb6b 100644 --- a/ql/ql/test/queries/style/ImplicitThis/Bad.qll +++ b/ql/ql/test/queries/style/ImplicitThis/Bad.qll @@ -7,5 +7,5 @@ class Foo extends string { string getBarWithThis() { result = this.getBar() } - string getBarWithoutThis() { result = getBar() } + string getBarWithoutThis() { result = getBar() } // $ Alert } diff --git a/ql/ql/test/queries/style/ImplicitThis/Bad2.qll b/ql/ql/test/queries/style/ImplicitThis/Bad2.qll index 27d7485ca4f..540c02f0921 100644 --- a/ql/ql/test/queries/style/ImplicitThis/Bad2.qll +++ b/ql/ql/test/queries/style/ImplicitThis/Bad2.qll @@ -5,5 +5,5 @@ class Foo extends string { string getBar() { result = "bar" } - string getBarWithoutThis() { result = getBar() } + string getBarWithoutThis() { result = getBar() } // $ Alert } diff --git a/ql/ql/test/queries/style/ImplicitThis/ImplicitThis.qlref b/ql/ql/test/queries/style/ImplicitThis/ImplicitThis.qlref index 0bdcd3b4b5b..f751b15e814 100644 --- a/ql/ql/test/queries/style/ImplicitThis/ImplicitThis.qlref +++ b/ql/ql/test/queries/style/ImplicitThis/ImplicitThis.qlref @@ -1 +1,2 @@ -queries/style/ImplicitThis.ql +query: queries/style/ImplicitThis.ql +postprocess: utils/test/InlineExpectationsTestQuery.ql diff --git a/ql/ql/test/queries/style/MissingParameterInQlDoc/Foo.qll b/ql/ql/test/queries/style/MissingParameterInQlDoc/Foo.qll index 13509dbe521..ffd21d59a5c 100644 --- a/ql/ql/test/queries/style/MissingParameterInQlDoc/Foo.qll +++ b/ql/ql/test/queries/style/MissingParameterInQlDoc/Foo.qll @@ -2,7 +2,7 @@ predicate test1(int param1, int param2, int param3) { none() } // OK /** `param1`, `par2` */ -predicate test2(int param1, int param2) { none() } // NOT OK - `par2` is not a parameter, and `param2` has no documentation +predicate test2(int param1, int param2) { none() } // $ Alert // NOT OK - `par2` is not a parameter, and `param2` has no documentation /** `param1`, `par2 + par3` */ predicate test3(int param1, int par2, int par3) { none() } // OK @@ -11,4 +11,4 @@ predicate test3(int param1, int par2, int par3) { none() } // OK predicate test4(int param1, int param2) { none() } // OK - the QLDoc mentions none of the parameters, that's OK /** the param1 parameter is mentioned in a non-code block, but the `par2` parameter is misspelled */ -predicate test5(int param1, int param2) { none() } // NOT OK - the `param1` parameter is "documented" in clear text, but `par2` is misspelled +predicate test5(int param1, int param2) { none() } // $ Alert // NOT OK - the `param1` parameter is "documented" in clear text, but `par2` is misspelled diff --git a/ql/ql/test/queries/style/MissingParameterInQlDoc/MissingParameterInQlDoc.qlref b/ql/ql/test/queries/style/MissingParameterInQlDoc/MissingParameterInQlDoc.qlref index 0539e4f5de2..a7d2f3d0a1d 100644 --- a/ql/ql/test/queries/style/MissingParameterInQlDoc/MissingParameterInQlDoc.qlref +++ b/ql/ql/test/queries/style/MissingParameterInQlDoc/MissingParameterInQlDoc.qlref @@ -1 +1,2 @@ -queries/style/MissingParameterInQlDoc.ql \ No newline at end of file +query: queries/style/MissingParameterInQlDoc.ql +postprocess: utils/test/InlineExpectationsTestQuery.ql diff --git a/ql/ql/test/queries/style/MissingQualityMetadata/MissingQualityMetadata.qlref b/ql/ql/test/queries/style/MissingQualityMetadata/MissingQualityMetadata.qlref index 6d7eb26bede..48abe277264 100644 --- a/ql/ql/test/queries/style/MissingQualityMetadata/MissingQualityMetadata.qlref +++ b/ql/ql/test/queries/style/MissingQualityMetadata/MissingQualityMetadata.qlref @@ -1 +1,2 @@ -queries/style/MissingQualityMetadata.ql +query: queries/style/MissingQualityMetadata.ql +postprocess: utils/test/InlineExpectationsTestQuery.ql diff --git a/ql/ql/test/queries/style/MissingQualityMetadata/testcases/BadQualityMaintainabilityWrongToplevel.ql b/ql/ql/test/queries/style/MissingQualityMetadata/testcases/BadQualityMaintainabilityWrongToplevel.ql index 3dd18771f95..0b1290de98b 100644 --- a/ql/ql/test/queries/style/MissingQualityMetadata/testcases/BadQualityMaintainabilityWrongToplevel.ql +++ b/ql/ql/test/queries/style/MissingQualityMetadata/testcases/BadQualityMaintainabilityWrongToplevel.ql @@ -8,7 +8,7 @@ * @tags quality * maintainability * error-handling - */ + */ // $ Alert import ql diff --git a/ql/ql/test/queries/style/MissingQualityMetadata/testcases/BadQualityMultipleTopLevel.ql b/ql/ql/test/queries/style/MissingQualityMetadata/testcases/BadQualityMultipleTopLevel.ql index a9a7b48b76c..4624b6d1076 100644 --- a/ql/ql/test/queries/style/MissingQualityMetadata/testcases/BadQualityMultipleTopLevel.ql +++ b/ql/ql/test/queries/style/MissingQualityMetadata/testcases/BadQualityMultipleTopLevel.ql @@ -8,7 +8,7 @@ * @tags quality * maintainability * reliability - */ + */ // $ Alert import ql diff --git a/ql/ql/test/queries/style/MissingQualityMetadata/testcases/BadQualityNoToplevel.ql b/ql/ql/test/queries/style/MissingQualityMetadata/testcases/BadQualityNoToplevel.ql index ad2ab5c1fb5..8c8bda6294e 100644 --- a/ql/ql/test/queries/style/MissingQualityMetadata/testcases/BadQualityNoToplevel.ql +++ b/ql/ql/test/queries/style/MissingQualityMetadata/testcases/BadQualityNoToplevel.ql @@ -7,7 +7,7 @@ * @id ql/quality-query-test * @tags quality * someothertag - */ + */ // $ Alert import ql diff --git a/ql/ql/test/queries/style/MissingQualityMetadata/testcases/BadQualityReliabilityWrongToplevel.ql b/ql/ql/test/queries/style/MissingQualityMetadata/testcases/BadQualityReliabilityWrongToplevel.ql index 53e84fb8a19..1a33baf6c51 100644 --- a/ql/ql/test/queries/style/MissingQualityMetadata/testcases/BadQualityReliabilityWrongToplevel.ql +++ b/ql/ql/test/queries/style/MissingQualityMetadata/testcases/BadQualityReliabilityWrongToplevel.ql @@ -8,7 +8,7 @@ * @tags quality * reliability * readability - */ + */ // $ Alert import ql diff --git a/ql/ql/test/queries/style/MissingSecurityMetadata/MissingSecurityMetadata.qlref b/ql/ql/test/queries/style/MissingSecurityMetadata/MissingSecurityMetadata.qlref index c697bcee82e..bd4295a6862 100644 --- a/ql/ql/test/queries/style/MissingSecurityMetadata/MissingSecurityMetadata.qlref +++ b/ql/ql/test/queries/style/MissingSecurityMetadata/MissingSecurityMetadata.qlref @@ -1 +1,2 @@ -queries/style/MissingSecurityMetadata.ql \ No newline at end of file +query: queries/style/MissingSecurityMetadata.ql +postprocess: utils/test/InlineExpectationsTestQuery.ql diff --git a/ql/ql/test/queries/style/MissingSecurityMetadata/testcases/BadNoSecurity.ql b/ql/ql/test/queries/style/MissingSecurityMetadata/testcases/BadNoSecurity.ql index d0562879831..a403812021e 100644 --- a/ql/ql/test/queries/style/MissingSecurityMetadata/testcases/BadNoSecurity.ql +++ b/ql/ql/test/queries/style/MissingSecurityMetadata/testcases/BadNoSecurity.ql @@ -7,7 +7,7 @@ * @precision very-high * @id ql/some-query * @tags quality - */ + */ // $ Alert import ql diff --git a/ql/ql/test/queries/style/MissingSecurityMetadata/testcases/BadNoSeverity.ql b/ql/ql/test/queries/style/MissingSecurityMetadata/testcases/BadNoSeverity.ql index f04fe81599a..47a12a1858a 100644 --- a/ql/ql/test/queries/style/MissingSecurityMetadata/testcases/BadNoSeverity.ql +++ b/ql/ql/test/queries/style/MissingSecurityMetadata/testcases/BadNoSeverity.ql @@ -7,7 +7,7 @@ * @id ql/some-query * @tags quality * security - */ + */ // $ Alert import ql diff --git a/ql/ql/test/queries/style/Misspelling/Misspelling.qlref b/ql/ql/test/queries/style/Misspelling/Misspelling.qlref index afbcaf951f3..ed9785fee3a 100644 --- a/ql/ql/test/queries/style/Misspelling/Misspelling.qlref +++ b/ql/ql/test/queries/style/Misspelling/Misspelling.qlref @@ -1 +1,2 @@ -queries/style/Misspelling.ql \ No newline at end of file +query: queries/style/Misspelling.ql +postprocess: utils/test/InlineExpectationsTestQuery.ql diff --git a/ql/ql/test/queries/style/Misspelling/Test.qll b/ql/ql/test/queries/style/Misspelling/Test.qll index b6619145f8d..1da75babe07 100644 --- a/ql/ql/test/queries/style/Misspelling/Test.qll +++ b/ql/ql/test/queries/style/Misspelling/Test.qll @@ -1,13 +1,13 @@ /** * A string that's deliberately mispelled (and so is that last word). - */ -class PublicallyAccessible extends string { - int numOccurences; // should be 'occurrences' + */ // $ Alert +class PublicallyAccessible extends string { // $ Alert + int numOccurences; // $ Alert // should be 'occurrences' PublicallyAccessible() { this = "publically" and numOccurences = 123 } // should be argument - predicate hasAgrument() { none() } + predicate hasAgrument() { none() } // $ Alert int getNum() { result = numOccurences } } @@ -15,8 +15,8 @@ class PublicallyAccessible extends string { /** * A class whose name contains a British-English spelling. * And here's the word 'colour'. - */ -class AnalysedInt extends int { + */ // $ Alert +class AnalysedInt extends int { // $ Alert AnalysedInt() { this = 7 } // 'analyses' should not be flagged diff --git a/ql/ql/test/queries/style/NonDocBlock/Foo.qll b/ql/ql/test/queries/style/NonDocBlock/Foo.qll index 99f957fa770..22fc0e3761a 100644 --- a/ql/ql/test/queries/style/NonDocBlock/Foo.qll +++ b/ql/ql/test/queries/style/NonDocBlock/Foo.qll @@ -1,13 +1,13 @@ /* * This should be QLDoc. - */ + */ // $ Alert /** * this is fine */ predicate foo() { any() } -/* Note: this is bad. */ +/* Note: this is bad. */ // $ Alert class Foo extends string { Foo() { this = "FOo" } } diff --git a/ql/ql/test/queries/style/NonDocBlock/NonDocBlock.qlref b/ql/ql/test/queries/style/NonDocBlock/NonDocBlock.qlref index b6dbdf50604..57118bb0ff7 100644 --- a/ql/ql/test/queries/style/NonDocBlock/NonDocBlock.qlref +++ b/ql/ql/test/queries/style/NonDocBlock/NonDocBlock.qlref @@ -1 +1,2 @@ -queries/style/NonDocBlock.ql \ No newline at end of file +query: queries/style/NonDocBlock.ql +postprocess: utils/test/InlineExpectationsTestQuery.ql diff --git a/ql/ql/test/queries/style/OmittableExists/OmittableExists.qlref b/ql/ql/test/queries/style/OmittableExists/OmittableExists.qlref index af9ad5ec40b..c606ef98425 100644 --- a/ql/ql/test/queries/style/OmittableExists/OmittableExists.qlref +++ b/ql/ql/test/queries/style/OmittableExists/OmittableExists.qlref @@ -1 +1,2 @@ -queries/style/OmittableExists.ql \ No newline at end of file +query: queries/style/OmittableExists.ql +postprocess: utils/test/InlineExpectationsTestQuery.ql diff --git a/ql/ql/test/queries/style/OmittableExists/Test.qll b/ql/ql/test/queries/style/OmittableExists/Test.qll index 517758a9dab..0312c86ec6e 100644 --- a/ql/ql/test/queries/style/OmittableExists/Test.qll +++ b/ql/ql/test/queries/style/OmittableExists/Test.qll @@ -17,7 +17,7 @@ class Location extends @location_default { } predicate test() { - exists(int i | aPredicate(i)) // BAD + exists(int i | aPredicate(i)) // $ Alert // BAD or exists(int i | aPredicate(i) or anotherPredicate(i)) // BAD [NOT DETECTED] or diff --git a/ql/ql/test/queries/style/QlRefInlineExpectations/QlRefInlineExpectations.expected b/ql/ql/test/queries/style/QlRefInlineExpectations/QlRefInlineExpectations.expected index 9605589e514..4725f6b634b 100644 --- a/ql/ql/test/queries/style/QlRefInlineExpectations/QlRefInlineExpectations.expected +++ b/ql/ql/test/queries/style/QlRefInlineExpectations/QlRefInlineExpectations.expected @@ -1 +1 @@ -| Test3.qlref:1:1:1:22 | query: ... uery.ql | Query test does not use inline test expectations. | +| Test3.qlref:1:1:1:23 | query: ... uery.ql | Query test does not use inline test expectations. | diff --git a/ql/ql/test/queries/style/QlRefInlineExpectations/Test3.qlref b/ql/ql/test/queries/style/QlRefInlineExpectations/Test3.qlref index 5582a96837a..d6af10c0fe6 100644 --- a/ql/ql/test/queries/style/QlRefInlineExpectations/Test3.qlref +++ b/ql/ql/test/queries/style/QlRefInlineExpectations/Test3.qlref @@ -1 +1 @@ -query: ProblemQuery.ql \ No newline at end of file +query: ProblemQuery.ql diff --git a/ql/ql/test/queries/style/RedundantCast/Foo.qll b/ql/ql/test/queries/style/RedundantCast/Foo.qll index d993f654bc4..4410d344c9f 100644 --- a/ql/ql/test/queries/style/RedundantCast/Foo.qll +++ b/ql/ql/test/queries/style/RedundantCast/Foo.qll @@ -2,10 +2,10 @@ class Foo extends string { Foo() { this = "Foo" } } -predicate test(Foo f) { f.(Foo).toString() = "X" } +predicate test(Foo f) { f.(Foo).toString() = "X" } // $ Alert -predicate test2(Foo a, Foo b) { a.(Foo) = b } +predicate test2(Foo a, Foo b) { a.(Foo) = b } // $ Alert predicate called(Foo a) { a.toString() = "X" } -predicate test3(string s) { called(s.(Foo)) } +predicate test3(string s) { called(s.(Foo)) } // $ Alert diff --git a/ql/ql/test/queries/style/RedundantCast/RedundantCast.qlref b/ql/ql/test/queries/style/RedundantCast/RedundantCast.qlref index 659062d3ae5..77bbbe67466 100644 --- a/ql/ql/test/queries/style/RedundantCast/RedundantCast.qlref +++ b/ql/ql/test/queries/style/RedundantCast/RedundantCast.qlref @@ -1 +1,2 @@ -queries/style/RedundantCast.ql +query: queries/style/RedundantCast.ql +postprocess: utils/test/InlineExpectationsTestQuery.ql diff --git a/ql/ql/test/queries/style/RedundantImport/D.qll b/ql/ql/test/queries/style/RedundantImport/D.qll index 1badf0ebbc5..ba5df313cdb 100644 --- a/ql/ql/test/queries/style/RedundantImport/D.qll +++ b/ql/ql/test/queries/style/RedundantImport/D.qll @@ -1,2 +1,2 @@ -import folder.A +import folder.A // $ Alert import folder.B diff --git a/ql/ql/test/queries/style/RedundantImport/RedundantImport.qlref b/ql/ql/test/queries/style/RedundantImport/RedundantImport.qlref index a2ff992e5cd..acacf6163e5 100644 --- a/ql/ql/test/queries/style/RedundantImport/RedundantImport.qlref +++ b/ql/ql/test/queries/style/RedundantImport/RedundantImport.qlref @@ -1 +1,2 @@ -queries/style/RedundantImport.ql \ No newline at end of file +query: queries/style/RedundantImport.ql +postprocess: utils/test/InlineExpectationsTestQuery.ql diff --git a/ql/ql/test/queries/style/RedundantOverride/RedundantOverride.qll b/ql/ql/test/queries/style/RedundantOverride/RedundantOverride.qll index 35df3b3194c..01d4e128615 100644 --- a/ql/ql/test/queries/style/RedundantOverride/RedundantOverride.qll +++ b/ql/ql/test/queries/style/RedundantOverride/RedundantOverride.qll @@ -6,7 +6,7 @@ module Test1 { } class Bar extends Foo { - override Foo pred() { result = Foo.super.pred() } // BAD + override Foo pred() { result = Foo.super.pred() } // $ Alert // BAD } } @@ -18,7 +18,7 @@ module Test2 { } class Bar extends Foo { - override Foo pred() { result = super.pred() } // BAD + override Foo pred() { result = super.pred() } // $ Alert // BAD } } @@ -107,7 +107,7 @@ module Test8 { } class Bar extends Foo { - override predicate pred(Foo f) { super.pred(f) } // BAD + override predicate pred(Foo f) { super.pred(f) } // $ Alert // BAD } } @@ -121,15 +121,15 @@ module Test9 { class Bar extends Foo { Bar() { this = 1 } - override Foo pred() { Foo.super.pred() = result } // BAD + override Foo pred() { Foo.super.pred() = result } // $ Alert // BAD } class Baz1 extends Foo, Bar { - override Foo pred() { Foo.super.pred() = result } // BAD + override Foo pred() { Foo.super.pred() = result } // $ Alert // BAD } class Baz2 extends Foo, Baz1 { - override Foo pred() { Baz1.super.pred() = result } // BAD + override Foo pred() { Baz1.super.pred() = result } // $ Alert // BAD } } @@ -147,7 +147,7 @@ module Test10 { } class Baz1 extends Foo, Bar { - override Foo pred() { result = Foo.super.pred() } // BAD + override Foo pred() { result = Foo.super.pred() } // $ Alert // BAD } } @@ -161,19 +161,19 @@ module Test11 { class Bar1 extends Foo { Bar1() { this = [1 .. 3] } - override Foo pred() { Foo.super.pred() = result } // BAD + override Foo pred() { Foo.super.pred() = result } // $ Alert // BAD } class Bar2 extends Foo, Bar1 { - override Foo pred() { Foo.super.pred() = result } // BAD + override Foo pred() { Foo.super.pred() = result } // $ Alert // BAD } class Bar3 extends Foo, Bar2 { - override Foo pred() { Bar2.super.pred() = result } // BAD + override Foo pred() { Bar2.super.pred() = result } // $ Alert // BAD } class Bar4 extends Bar2, Bar3 { - override Foo pred() { result = Bar2.super.pred() } // BAD + override Foo pred() { result = Bar2.super.pred() } // $ Alert // BAD } class Bar5 extends Foo { diff --git a/ql/ql/test/queries/style/RedundantOverride/RedundantOverride.qlref b/ql/ql/test/queries/style/RedundantOverride/RedundantOverride.qlref index aca59af1cce..ac16aebc2e7 100644 --- a/ql/ql/test/queries/style/RedundantOverride/RedundantOverride.qlref +++ b/ql/ql/test/queries/style/RedundantOverride/RedundantOverride.qlref @@ -1 +1,2 @@ -queries/style/RedundantOverride.ql +query: queries/style/RedundantOverride.ql +postprocess: utils/test/InlineExpectationsTestQuery.ql diff --git a/ql/ql/test/queries/style/SwappedParameterNames/SwappedParameterNames.qlref b/ql/ql/test/queries/style/SwappedParameterNames/SwappedParameterNames.qlref index cab8c347410..78ad77024ca 100644 --- a/ql/ql/test/queries/style/SwappedParameterNames/SwappedParameterNames.qlref +++ b/ql/ql/test/queries/style/SwappedParameterNames/SwappedParameterNames.qlref @@ -1 +1,2 @@ -queries/style/SwappedParameterNames.ql \ No newline at end of file +query: queries/style/SwappedParameterNames.ql +postprocess: utils/test/InlineExpectationsTestQuery.ql diff --git a/ql/ql/test/queries/style/SwappedParameterNames/Test.qll b/ql/ql/test/queries/style/SwappedParameterNames/Test.qll index 5c8083d3098..0ee3760c7cb 100644 --- a/ql/ql/test/queries/style/SwappedParameterNames/Test.qll +++ b/ql/ql/test/queries/style/SwappedParameterNames/Test.qll @@ -9,5 +9,5 @@ class Correct extends Sup { } class Wrong extends Sup { - override predicate step(Expr succ, Expr pred) { none() } // <- swapped parameter names + override predicate step(Expr succ, Expr pred) { none() } // $ Alert // <- swapped parameter names } diff --git a/ql/ql/test/queries/style/UseInstanceofExtension/Foo.qll b/ql/ql/test/queries/style/UseInstanceofExtension/Foo.qll index b58cb3f93e3..b6479e6fc3a 100644 --- a/ql/ql/test/queries/style/UseInstanceofExtension/Foo.qll +++ b/ql/ql/test/queries/style/UseInstanceofExtension/Foo.qll @@ -4,7 +4,7 @@ class Range extends string { string getAChild() { result = "test" } } -class Inst extends string { +class Inst extends string { // $ Alert Range range; Inst() { this = range } @@ -12,13 +12,13 @@ class Inst extends string { string getAChild() { result = range.getAChild() } } -class Inst2 extends string { +class Inst2 extends string { // $ Alert Inst2() { this instanceof Range } string getAChild() { result = this.(Range).getAChild() } } -class Inst3 extends string { +class Inst3 extends string { // $ Alert Range range; Inst3() { this = range } @@ -26,6 +26,6 @@ class Inst3 extends string { Range getRange() { result = range } } -class Inst4 extends string { +class Inst4 extends string { // $ Alert Inst4() { this instanceof Range } } diff --git a/ql/ql/test/queries/style/UseInstanceofExtension/UseInstanceofExtension.qlref b/ql/ql/test/queries/style/UseInstanceofExtension/UseInstanceofExtension.qlref index 4b8a6515787..d895947b87b 100644 --- a/ql/ql/test/queries/style/UseInstanceofExtension/UseInstanceofExtension.qlref +++ b/ql/ql/test/queries/style/UseInstanceofExtension/UseInstanceofExtension.qlref @@ -1 +1,2 @@ -queries/style/UseInstanceofExtension.ql \ No newline at end of file +query: queries/style/UseInstanceofExtension.ql +postprocess: utils/test/InlineExpectationsTestQuery.ql diff --git a/ql/ql/test/queries/style/UseSetLiteral/UseSetLiteral.qlref b/ql/ql/test/queries/style/UseSetLiteral/UseSetLiteral.qlref index d4047ebc29f..545dc8d4842 100644 --- a/ql/ql/test/queries/style/UseSetLiteral/UseSetLiteral.qlref +++ b/ql/ql/test/queries/style/UseSetLiteral/UseSetLiteral.qlref @@ -1 +1,2 @@ -queries/style/UseSetLiteral.ql \ No newline at end of file +query: queries/style/UseSetLiteral.ql +postprocess: utils/test/InlineExpectationsTestQuery.ql diff --git a/ql/ql/test/queries/style/UseSetLiteral/test.qll b/ql/ql/test/queries/style/UseSetLiteral/test.qll index fcc581c3e8c..0fd1dab6ddd 100644 --- a/ql/ql/test/queries/style/UseSetLiteral/test.qll +++ b/ql/ql/test/queries/style/UseSetLiteral/test.qll @@ -4,7 +4,7 @@ predicate test1(int a) { a = 1 or // BAD a = 2 or a = 3 or - a = 4 + a = 4 // $ Alert } predicate test2(int a) { @@ -30,7 +30,7 @@ predicate test5() { test1(1) or // BAD test1(2) or test1(3) or - test1(4) + test1(4) // $ Alert } predicate test6() { @@ -44,7 +44,7 @@ int test7() { 1 = result or // BAD 2 = result or 3 = result or - 4 = result + 4 = result // $ Alert } predicate test8() { @@ -62,19 +62,19 @@ class MyTest8Class extends int { this = 1 or // BAD this = 2 or this = 3 or - this = 4 + this = 4 // $ Alert ) and ( s = "1" or // BAD s = "2" or s = "3" or - s = "4" + s = "4" // $ Alert ) and exists(float f | f = 1.0 or // BAD f = 1.5 or f = 2.0 or - f = 2.5 + f = 2.5 // $ Alert ) } @@ -89,7 +89,7 @@ predicate test9(MyTest8Class c) { c.is(1) or // BAD c.is(2) or c.is(3) or - c.is(4) + c.is(4) // $ Alert } predicate test10(MyTest8Class c) { @@ -133,5 +133,5 @@ predicate test14(int a) { (a = 2 or a = 3) or a = 4 - ) + ) // $ Alert } diff --git a/ql/ql/test/queries/style/ValidatePredicateGetReturns/ValidatePredicateGetReturns.qlref b/ql/ql/test/queries/style/ValidatePredicateGetReturns/ValidatePredicateGetReturns.qlref index e116f69d6b2..7a89245d787 100644 --- a/ql/ql/test/queries/style/ValidatePredicateGetReturns/ValidatePredicateGetReturns.qlref +++ b/ql/ql/test/queries/style/ValidatePredicateGetReturns/ValidatePredicateGetReturns.qlref @@ -1 +1,2 @@ -queries/style/ValidatePredicateGetReturns.ql +query: queries/style/ValidatePredicateGetReturns.ql +postprocess: utils/test/InlineExpectationsTestQuery.ql diff --git a/ql/ql/test/queries/style/ValidatePredicateGetReturns/test.qll b/ql/ql/test/queries/style/ValidatePredicateGetReturns/test.qll index 2cc4dec64d2..e9c34eb94a6 100644 --- a/ql/ql/test/queries/style/ValidatePredicateGetReturns/test.qll +++ b/ql/ql/test/queries/style/ValidatePredicateGetReturns/test.qll @@ -1,7 +1,7 @@ import ql // NOT OK -- Predicate starts with "get" but does not return a value -predicate getValue() { none() } +predicate getValue() { none() } // $ Alert // OK -- starts with get and returns a value string getData() { result = "data" } @@ -22,13 +22,13 @@ predicate getvalue() { none() } predicate retrieveValue() { none() } // NOT OK -- starts with get and does not return value -predicate getImplementation2() { none() } +predicate getImplementation2() { none() } // $ Alert // NOT OK -- is an alias for a predicate which does not have a return value -predicate getAlias2 = getImplementation2/0; +predicate getAlias2 = getImplementation2/0; // $ Alert // NOT OK -- starts with as and does not return value -predicate asValue() { none() } +predicate asValue() { none() } // $ Alert // OK -- starts with as but followed by a lowercase letter, probably should be ignored predicate assessment() { none() } @@ -45,7 +45,7 @@ HiddenType getInjectableCompositeActionNode() { predicate implementation4() { none() } // NOT OK -- is an alias -predicate getAlias4 = implementation4/0; +predicate getAlias4 = implementation4/0; // $ Alert // OK -- is an alias predicate alias5 = implementation4/0; @@ -58,7 +58,7 @@ predicate edge(int x, int y) { none() } int getDistance(int x) = shortestDistances(root/0, edge/2)(_, x, result) // NOT OK -- Higher-order predicate that does not return a value even though has 'get' in the name -predicate getDistance2(int x, int y) = shortestDistances(root/0, edge/2)(_, x, y) +predicate getDistance2(int x, int y) = shortestDistances(root/0, edge/2)(_, x, y) // $ Alert // OK predicate unresolvedAlias = unresolved/0; diff --git a/ruby/ql/lib/change-notes/2026-06-15-case-else-branch.md b/ruby/ql/lib/change-notes/2026-06-15-case-else-branch.md new file mode 100644 index 00000000000..a927f1e2c28 --- /dev/null +++ b/ruby/ql/lib/change-notes/2026-06-15-case-else-branch.md @@ -0,0 +1,4 @@ +--- +category: breaking +--- +* The `else` branch of a `case` expression is no longer represented as a `StmtSequence` directly. Instead, a new `CaseElseBranch` AST node wraps the body (a `StmtSequence`). `CaseExpr.getElseBranch()` now returns a `CaseElseBranch`, and the body of the else branch can be accessed via `CaseElseBranch.getBody()`. diff --git a/ruby/ql/lib/codeql/ruby/ast/Control.qll b/ruby/ql/lib/codeql/ruby/ast/Control.qll index 5d83e7a62fd..ea54d355469 100644 --- a/ruby/ql/lib/codeql/ruby/ast/Control.qll +++ b/ruby/ql/lib/codeql/ruby/ast/Control.qll @@ -377,18 +377,18 @@ class CaseExpr extends ControlExpr instanceof CaseExprImpl { /** * Gets the `n`th branch of this case expression, either a `WhenClause`, an - * `InClause`, or a `StmtSequence`. + * `InClause`, or a `CaseElseBranch`. */ final AstNode getBranch(int n) { result = super.getBranch(n) } /** * Gets a branch of this case expression, either a `WhenClause`, an - * `InClause`, or a `StmtSequence`. + * `InClause`, or a `CaseElseBranch`. */ final AstNode getABranch() { result = this.getBranch(_) } /** Gets the `else` branch of this case expression, if any. */ - final StmtSequence getElseBranch() { result = this.getABranch() } + final CaseElseBranch getElseBranch() { result = this.getABranch() } /** * Gets the number of branches of this case expression. @@ -533,6 +533,30 @@ class InClause extends AstNode instanceof InClauseImpl { } } +/** + * An `else` branch of a `case` expression. + * ```rb + * case foo + * when 1 then puts "one" + * else puts "other" + * end + * ``` + */ +class CaseElseBranch extends AstNode instanceof CaseElseBranchImpl { + final override string getAPrimaryQlClass() { result = "CaseElseBranch" } + + /** Gets the body of this else branch. */ + final StmtSequence getBody() { result = super.getBody() } + + final override string toString() { result = "else ..." } + + final override AstNode getAChild(string pred) { + result = AstNode.super.getAChild(pred) + or + pred = "getBody" and result = this.getBody() + } +} + /** * A one-line pattern match using the `=>` operator. For example: * ```rb diff --git a/ruby/ql/lib/codeql/ruby/ast/internal/AST.qll b/ruby/ql/lib/codeql/ruby/ast/internal/AST.qll index 17d4a6bb8b6..4b3535c490d 100644 --- a/ruby/ql/lib/codeql/ruby/ast/internal/AST.qll +++ b/ruby/ql/lib/codeql/ruby/ast/internal/AST.qll @@ -113,6 +113,9 @@ private module Cached { TBraceBlockSynth(Ast::AstNode parent, int i) { mkSynthChild(BraceBlockKind(), parent, i) } or TBraceBlockReal(Ruby::Block g) { not g.getParent() instanceof Ruby::Lambda } or TBreakStmt(Ruby::Break g) or + TCaseElseBranchSynth(Ast::AstNode parent, int i) { + mkSynthChild(CaseElseBranchKind(), parent, i) + } or TCaseEqExpr(Ruby::Binary g) { g instanceof @ruby_binary_equalequalequal } or TCaseExpr(Ruby::Case g) or TCaseMatchReal(Ruby::CaseMatch g) or @@ -400,14 +403,15 @@ private module Cached { class TAstNodeSynth = TAddExprSynth or TAssignExprSynth or TBitwiseAndExprSynth or TBitwiseOrExprSynth or TBitwiseXorExprSynth or TBraceBlockSynth or TBodyStmtSynth or TBooleanLiteralSynth or - TCaseMatchSynth or TClassVariableAccessSynth or TConstantReadAccessSynth or - TConstantWriteAccessSynth or TDivExprSynth or TElseSynth or TExponentExprSynth or - TGlobalVariableAccessSynth or TIfSynth or TInClauseSynth or TInstanceVariableAccessSynth or - TIntegerLiteralSynth or TLShiftExprSynth or TLocalVariableAccessSynth or - TLogicalAndExprSynth or TLogicalOrExprSynth or TMethodCallSynth or TModuloExprSynth or - TMulExprSynth or TNilLiteralSynth or TRShiftExprSynth or TRangeLiteralSynth or TSelfSynth or - TSimpleParameterSynth or TSplatExprSynth or THashSplatExprSynth or TStmtSequenceSynth or - TSubExprSynth or TPairSynth or TSimpleSymbolLiteralSynth; + TCaseElseBranchSynth or TCaseMatchSynth or TClassVariableAccessSynth or + TConstantReadAccessSynth or TConstantWriteAccessSynth or TDivExprSynth or TElseSynth or + TExponentExprSynth or TGlobalVariableAccessSynth or TIfSynth or TInClauseSynth or + TInstanceVariableAccessSynth or TIntegerLiteralSynth or TLShiftExprSynth or + TLocalVariableAccessSynth or TLogicalAndExprSynth or TLogicalOrExprSynth or + TMethodCallSynth or TModuloExprSynth or TMulExprSynth or TNilLiteralSynth or + TRShiftExprSynth or TRangeLiteralSynth or TSelfSynth or TSimpleParameterSynth or + TSplatExprSynth or THashSplatExprSynth or TStmtSequenceSynth or TSubExprSynth or + TPairSynth or TSimpleSymbolLiteralSynth; /** * Gets the underlying TreeSitter entity for a given AST node. This does not @@ -598,6 +602,8 @@ private module Cached { or result = TBraceBlockSynth(parent, i) or + result = TCaseElseBranchSynth(parent, i) + or result = TCaseMatchSynth(parent, i) or result = TClassVariableAccessSynth(parent, i, _) @@ -718,6 +724,8 @@ TAstNodeReal fromGenerated(Ruby::AstNode n) { n = toGenerated(result) } class TCall = TMethodCall or TYieldCall; +class TCaseElseBranch = TCaseElseBranchSynth; + class TCaseMatch = TCaseMatchReal or TCaseMatchSynth; class TCase = TCaseExpr or TCaseMatch; diff --git a/ruby/ql/lib/codeql/ruby/ast/internal/Control.qll b/ruby/ql/lib/codeql/ruby/ast/internal/Control.qll index dd57a0d197d..00076ba996a 100644 --- a/ruby/ql/lib/codeql/ruby/ast/internal/Control.qll +++ b/ruby/ql/lib/codeql/ruby/ast/internal/Control.qll @@ -19,8 +19,11 @@ class CaseWhenClause extends CaseExprImpl, TCaseExpr { final override Expr getValue() { toGenerated(result) = g.getValue() } final override AstNode getBranch(int n) { - toGenerated(result) = g.getChild(n) or - toGenerated(result) = g.getChild(n) + // When branches map directly to WhenClause nodes + toGenerated(result) = g.getChild(n) and not g.getChild(n) instanceof Ruby::Else + or + // The else branch is wrapped in a synthesized CaseElseBranch node + g.getChild(n) instanceof Ruby::Else and result = getSynthChild(this, n) } } @@ -34,7 +37,8 @@ class CaseMatch extends CaseExprImpl, TCaseMatchReal { final override AstNode getBranch(int n) { toGenerated(result) = g.getClauses(n) or - n = count(g.getClauses(_)) and toGenerated(result) = g.getElse() + // The else branch is wrapped in a synthesized CaseElseBranch node + n = count(g.getClauses(_)) and exists(g.getElse()) and result = getSynthChild(this, n) } } @@ -87,3 +91,9 @@ class InClauseSynth extends InClauseImpl, TInClauseSynth { final override predicate hasUnlessCondition() { none() } } + +class CaseElseBranchImpl extends AstNode, TCaseElseBranch { + CaseElseBranchImpl() { this = TCaseElseBranchSynth(_, _) } + + final StmtSequence getBody() { synthChild(this, 0, result) } +} diff --git a/ruby/ql/lib/codeql/ruby/ast/internal/Synthesis.qll b/ruby/ql/lib/codeql/ruby/ast/internal/Synthesis.qll index ca40a4160d2..081cbd01a38 100644 --- a/ruby/ql/lib/codeql/ruby/ast/internal/Synthesis.qll +++ b/ruby/ql/lib/codeql/ruby/ast/internal/Synthesis.qll @@ -22,6 +22,7 @@ newtype TSynthKind = BodyStmtKind() or BooleanLiteralKind(boolean value) { value = true or value = false } or BraceBlockKind() or + CaseElseBranchKind() or CaseMatchKind() or ClassVariableAccessKind(ClassVariable v) or DefinedExprKind() or @@ -80,6 +81,8 @@ class SynthKind extends TSynthKind { or this = BraceBlockKind() and result = "BraceBlockKind" or + this = CaseElseBranchKind() and result = "CaseElseBranchKind" + or this = CaseMatchKind() and result = "CaseMatchKind" or this = ClassVariableAccessKind(_) and result = "ClassVariableAccessKind" @@ -1840,7 +1843,7 @@ private module TestPatternDesugar { or child = SynthChild(InClauseKind()) and i = 1 or - child = SynthChild(ElseKind()) and i = 2 + child = SynthChild(CaseElseBranchKind()) and i = 2 ) or parent = TInClauseSynth(case, 1) and @@ -1851,7 +1854,11 @@ private module TestPatternDesugar { child = SynthChild(BooleanLiteralKind(true)) and i = 1 ) or - parent = TElseSynth(case, 2) and + parent = TCaseElseBranchSynth(case, 2) and + child = SynthChild(ElseKind()) and + i = 0 + or + parent = TElseSynth(TCaseElseBranchSynth(case, 2), 0) and child = SynthChild(BooleanLiteralKind(false)) and i = 0 ) @@ -1994,3 +2001,61 @@ private module CallableBodySynthesis { } } } + +private module CaseElseBranchSynthesis { + pragma[nomagic] + private predicate caseElseBranchSynthesis(AstNode parent, int i, Child child) { + // Wrap the else branch of a real `case`/`when` expression + exists(Ruby::Case g, Ruby::Else elseNode, int elseIndex | + elseNode = g.getChild(elseIndex) and + ( + // Create the CaseElseBranch wrapper node at the else index + parent = TCaseExpr(g) and + child = SynthChild(CaseElseBranchKind()) and + i = elseIndex + or + // The body of the CaseElseBranch is the Else node + parent = TCaseElseBranchSynth(TCaseExpr(g), elseIndex) and + child = RealChildRef(TElseReal(elseNode)) and + i = 0 + ) + ) + or + // Wrap the else branch of a real `case`/`in` expression + exists(Ruby::CaseMatch g, Ruby::Else elseNode, int elseIndex | + elseNode = g.getElse() and + elseIndex = count(g.getClauses(_)) and + ( + // Create the CaseElseBranch wrapper node at the else index + parent = TCaseMatchReal(g) and + child = SynthChild(CaseElseBranchKind()) and + i = elseIndex + or + // The body of the CaseElseBranch is the Else node + parent = TCaseElseBranchSynth(TCaseMatchReal(g), elseIndex) and + child = RealChildRef(TElseReal(elseNode)) and + i = 0 + ) + ) + } + + private class CaseElseBranchSynthesisImpl extends Synthesis { + final override predicate child(AstNode parent, int i, Child child) { + caseElseBranchSynthesis(parent, i, child) + } + + final override predicate location(AstNode n, Location l) { + // Give the CaseElseBranch the location of the underlying Else node + exists(Ruby::Case g, int elseIndex | + n = TCaseElseBranchSynth(TCaseExpr(g), elseIndex) and + l = g.getChild(elseIndex).getLocation() + ) + or + exists(Ruby::CaseMatch g, int elseIndex | + elseIndex = count(g.getClauses(_)) and + n = TCaseElseBranchSynth(TCaseMatchReal(g), elseIndex) and + l = g.getElse().getLocation() + ) + } + } +} diff --git a/ruby/ql/lib/codeql/ruby/ast/internal/Variable.qll b/ruby/ql/lib/codeql/ruby/ast/internal/Variable.qll index 6e92b54c246..5ff48191534 100644 --- a/ruby/ql/lib/codeql/ruby/ast/internal/Variable.qll +++ b/ruby/ql/lib/codeql/ruby/ast/internal/Variable.qll @@ -11,6 +11,17 @@ private import codeql.ruby.ast.internal.Pattern private import codeql.ruby.ast.internal.Scope private import codeql.ruby.ast.internal.Synthesis +private Ruby::AstNode getAssignmentParent(Ruby::AstNode n) { + result = n.getParent() and + ( + result instanceof Ruby::DestructuredLeftAssignment + or + result instanceof Ruby::LeftAssignmentList + or + result instanceof Ruby::RestAssignment + ) +} + /** * Holds if `n` is in the left-hand-side of an explicit assignment `assignment`. */ @@ -19,16 +30,7 @@ predicate explicitAssignmentNode(Ruby::AstNode n, Ruby::AstNode assignment) { or n = assignment.(Ruby::OperatorAssignment).getLeft() or - exists(Ruby::AstNode parent | - parent = n.getParent() and - explicitAssignmentNode(parent, assignment) - | - parent instanceof Ruby::DestructuredLeftAssignment - or - parent instanceof Ruby::LeftAssignmentList - or - parent instanceof Ruby::RestAssignment - ) + explicitAssignmentNode(getAssignmentParent(n), assignment) } /** Holds if `n` is inside an implicit assignment. */ @@ -49,7 +51,7 @@ predicate implicitAssignmentNode(Ruby::AstNode n) { or n = any(Ruby::For for).getPattern() or - implicitAssignmentNode(n.getParent()) + implicitAssignmentNode(getAssignmentParent(n)) } /** Holds if `n` is inside a parameter. */ diff --git a/ruby/ql/lib/codeql/ruby/controlflow/internal/ControlFlowGraphImpl.qll b/ruby/ql/lib/codeql/ruby/controlflow/internal/ControlFlowGraphImpl.qll index 9658c51d673..e66e8bad003 100644 --- a/ruby/ql/lib/codeql/ruby/controlflow/internal/ControlFlowGraphImpl.qll +++ b/ruby/ql/lib/codeql/ruby/controlflow/internal/ControlFlowGraphImpl.qll @@ -498,6 +498,16 @@ module Trees { } } + private class CaseElseBranchTree extends ControlFlowTree instanceof CaseElseBranch { + final override predicate propagatesAbnormal(AstNode child) { child = super.getBody() } + + final override predicate first(AstNode first) { first(super.getBody(), first) } + + final override predicate last(AstNode last, Completion c) { last(super.getBody(), last, c) } + + final override predicate succ(AstNode pred, AstNode succ, Completion c) { none() } + } + private class PatternVariableAccessTree extends LocalVariableAccessTree instanceof LocalVariableWriteAccess, CasePattern { diff --git a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPrivate.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPrivate.qll index fb5ce7b0145..9646592c0c2 100644 --- a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPrivate.qll +++ b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPrivate.qll @@ -68,9 +68,9 @@ private CfgNodes::ExprCfgNode getALastEvalNode(CfgNodes::ExprCfgNode n) { result = branch.(CfgNodes::ExprNodes::InClauseCfgNode).getBody() or result = branch.(CfgNodes::ExprNodes::WhenClauseCfgNode).getBody() - or - result = branch ) + or + result.getAstNode() = n.(CfgNodes::ExprNodes::CaseExprCfgNode).getExpr().getElseBranch().getBody() } /** diff --git a/ruby/ql/test/library-tests/ast/Ast.expected b/ruby/ql/test/library-tests/ast/Ast.expected index c391b7f584d..da0a58f5d7d 100644 --- a/ruby/ql/test/library-tests/ast/Ast.expected +++ b/ruby/ql/test/library-tests/ast/Ast.expected @@ -815,8 +815,9 @@ control/cases.rb: # 11| getPattern: [LocalVariableAccess] d # 11| getBody: [StmtSequence] then ... # 12| getStmt: [IntegerLiteral] 200 -# 13| getBranch/getElseBranch: [StmtSequence] else ... -# 14| getStmt: [IntegerLiteral] 300 +# 13| getBranch/getElseBranch: [CaseElseBranch] else ... +# 13| getBody: [StmtSequence] else ... +# 14| getStmt: [IntegerLiteral] 300 # 18| getStmt: [CaseExpr] case ... # 19| getBranch: [WhenClause] when ... # 19| getPattern: [GTExpr] ... > ... @@ -843,8 +844,9 @@ control/cases.rb: # 27| getPattern: [IntegerLiteral] 5 # 27| getBody: [StmtSequence] then ... # 27| getStmt: [BooleanLiteral] true -# 28| getBranch/getElseBranch: [StmtSequence] else ... -# 28| getStmt: [BooleanLiteral] false +# 28| getBranch/getElseBranch: [CaseElseBranch] else ... +# 28| getBody: [StmtSequence] else ... +# 28| getStmt: [BooleanLiteral] false # 31| getStmt: [CaseExpr] case ... # 31| getValue: [MethodCall] call to expr # 31| getReceiver: [SelfVariableAccess] self @@ -862,8 +864,9 @@ control/cases.rb: # 34| getAnOperand/getArgument/getGreaterOperand/getRightOperand: [IntegerLiteral] 0 # 34| getBody: [StmtSequence] then ... # 35| getStmt: [BooleanLiteral] true -# 36| getBranch/getElseBranch: [StmtSequence] else ... -# 36| getStmt: [BooleanLiteral] false +# 36| getBranch/getElseBranch: [CaseElseBranch] else ... +# 36| getBody: [StmtSequence] else ... +# 36| getStmt: [BooleanLiteral] false # 39| getStmt: [CaseExpr] case ... # 39| getValue: [MethodCall] call to expr # 39| getReceiver: [SelfVariableAccess] self diff --git a/ruby/ql/test/library-tests/ast/AstDesugar.expected b/ruby/ql/test/library-tests/ast/AstDesugar.expected index 40594888db1..bd350dc0070 100644 --- a/ruby/ql/test/library-tests/ast/AstDesugar.expected +++ b/ruby/ql/test/library-tests/ast/AstDesugar.expected @@ -389,8 +389,9 @@ control/cases.rb: # 160| getPrefixElement: [IntegerLiteral] 1 # 160| getPrefixElement: [IntegerLiteral] 2 # 160| getBody: [BooleanLiteral] true -# 160| getBranch/getElseBranch: [StmtSequence] else ... -# 160| getStmt: [BooleanLiteral] false +# 160| getBranch/getElseBranch: [CaseElseBranch] else ... +# 160| getBody: [StmtSequence] else ... +# 160| getStmt: [BooleanLiteral] false # 162| [MatchPattern] ... => ... # 162| getDesugared: [CaseExpr] case ... # 162| getValue: [MethodCall] call to expr diff --git a/ruby/ql/test/library-tests/ast/control/CaseExpr.ql b/ruby/ql/test/library-tests/ast/control/CaseExpr.ql index 09a64e767e7..f03bdd1e534 100644 --- a/ruby/ql/test/library-tests/ast/control/CaseExpr.ql +++ b/ruby/ql/test/library-tests/ast/control/CaseExpr.ql @@ -4,7 +4,7 @@ query predicate caseValues(CaseExpr c, Expr value) { value = c.getValue() } query predicate caseNoValues(CaseExpr c) { not exists(c.getValue()) } -query predicate caseElseBranches(CaseExpr c, StmtSequence elseBranch) { +query predicate caseElseBranches(CaseExpr c, CaseElseBranch elseBranch) { elseBranch = c.getElseBranch() } diff --git a/ruby/ql/test/library-tests/modules/methods.expected b/ruby/ql/test/library-tests/modules/methods.expected index 272081218c5..4fea527074f 100644 --- a/ruby/ql/test/library-tests/modules/methods.expected +++ b/ruby/ql/test/library-tests/modules/methods.expected @@ -975,6 +975,7 @@ enclosingMethod | calls.rb:354:9:354:9 | x | calls.rb:347:1:363:3 | pattern_dispatch | | calls.rb:354:9:354:18 | call to instance | calls.rb:347:1:363:3 | pattern_dispatch | | calls.rb:355:5:355:8 | else ... | calls.rb:347:1:363:3 | pattern_dispatch | +| calls.rb:355:5:355:8 | else ... | calls.rb:347:1:363:3 | pattern_dispatch | | calls.rb:358:5:362:7 | case ... | calls.rb:347:1:363:3 | pattern_dispatch | | calls.rb:358:10:358:10 | x | calls.rb:347:1:363:3 | pattern_dispatch | | calls.rb:359:9:359:29 | in ... then ... | calls.rb:347:1:363:3 | pattern_dispatch | diff --git a/ruby/ql/test/library-tests/modules/modules.expected b/ruby/ql/test/library-tests/modules/modules.expected index 09a2236772a..f599ef11c6a 100644 --- a/ruby/ql/test/library-tests/modules/modules.expected +++ b/ruby/ql/test/library-tests/modules/modules.expected @@ -1182,6 +1182,7 @@ enclosingModule | calls.rb:354:9:354:9 | x | calls.rb:1:1:667:52 | calls.rb | | calls.rb:354:9:354:18 | call to instance | calls.rb:1:1:667:52 | calls.rb | | calls.rb:355:5:355:8 | else ... | calls.rb:1:1:667:52 | calls.rb | +| calls.rb:355:5:355:8 | else ... | calls.rb:1:1:667:52 | calls.rb | | calls.rb:358:5:362:7 | case ... | calls.rb:1:1:667:52 | calls.rb | | calls.rb:358:10:358:10 | x | calls.rb:1:1:667:52 | calls.rb | | calls.rb:359:9:359:29 | in ... then ... | calls.rb:1:1:667:52 | calls.rb | diff --git a/ruby/ql/test/library-tests/variables/parameter.expected b/ruby/ql/test/library-tests/variables/parameter.expected index c04df71117d..437e39546eb 100644 --- a/ruby/ql/test/library-tests/variables/parameter.expected +++ b/ruby/ql/test/library-tests/variables/parameter.expected @@ -29,6 +29,8 @@ parameterVariable | scopes.rb:2:14:2:14 | x | scopes.rb:2:14:2:14 | x | | scopes.rb:9:14:9:14 | x | scopes.rb:9:14:9:14 | x | | scopes.rb:69:15:69:15 | x | scopes.rb:69:15:69:15 | x | +| scopes.rb:80:13:80:17 | value | scopes.rb:80:13:80:17 | value | +| scopes.rb:84:11:84:13 | msg | scopes.rb:84:11:84:13 | msg | | ssa.rb:1:7:1:7 | b | ssa.rb:1:7:1:7 | b | | ssa.rb:18:8:18:8 | x | ssa.rb:18:8:18:8 | x | | ssa.rb:25:8:25:15 | elements | ssa.rb:25:8:25:15 | elements | diff --git a/ruby/ql/test/library-tests/variables/scopes.rb b/ruby/ql/test/library-tests/variables/scopes.rb index c37146cd681..50a8ad9b107 100644 --- a/ruby/ql/test/library-tests/variables/scopes.rb +++ b/ruby/ql/test/library-tests/variables/scopes.rb @@ -70,4 +70,20 @@ module ParameterShadowing puts x end puts x # prints `1`, not `3` -end \ No newline at end of file +end + +class RescueSetter + def name + @name + end + + def name=(value) + @name = value + end + + def foo(msg) + raise msg + rescue => self.name # calls `name=` + :caught + end +end diff --git a/ruby/ql/test/library-tests/variables/ssa.expected b/ruby/ql/test/library-tests/variables/ssa.expected index ab68d17ac2a..69222157b05 100644 --- a/ruby/ql/test/library-tests/variables/ssa.expected +++ b/ruby/ql/test/library-tests/variables/ssa.expected @@ -86,12 +86,12 @@ definition | parameters.rb:59:20:59:20 | a | parameters.rb:59:20:59:20 | a | | parameters.rb:59:23:59:23 | b | parameters.rb:59:23:59:23 | b | | parameters.rb:59:25:59:25 | c | parameters.rb:59:25:59:25 | c | -| scopes.rb:1:1:73:3 | self (scopes.rb) | scopes.rb:1:1:73:3 | self | -| scopes.rb:2:9:6:3 | self | scopes.rb:1:1:73:3 | self | +| scopes.rb:1:1:89:4 | self (scopes.rb) | scopes.rb:1:1:89:4 | self | +| scopes.rb:2:9:6:3 | self | scopes.rb:1:1:89:4 | self | | scopes.rb:4:4:4:4 | a | scopes.rb:4:4:4:4 | a | | scopes.rb:7:1:7:1 | a | scopes.rb:7:1:7:1 | a | | scopes.rb:9:9:18:3 | a | scopes.rb:7:1:7:1 | a | -| scopes.rb:9:9:18:3 | self | scopes.rb:1:1:73:3 | self | +| scopes.rb:9:9:18:3 | self | scopes.rb:1:1:89:4 | self | | scopes.rb:11:4:11:4 | a | scopes.rb:7:1:7:1 | a | | scopes.rb:13:4:13:4 | a | scopes.rb:7:1:7:1 | a | | scopes.rb:13:7:13:7 | b | scopes.rb:13:7:13:7 | b | @@ -111,6 +111,12 @@ definition | scopes.rb:68:3:68:4 | xs | scopes.rb:68:3:68:4 | xs | | scopes.rb:69:11:71:5 | self | scopes.rb:66:1:73:3 | self | | scopes.rb:69:15:69:15 | x | scopes.rb:69:15:69:15 | x | +| scopes.rb:75:1:89:3 | self (RescueSetter) | scopes.rb:75:1:89:3 | self | +| scopes.rb:76:3:78:5 | self (name) | scopes.rb:76:3:78:5 | self | +| scopes.rb:80:3:82:5 | self (name=) | scopes.rb:80:3:82:5 | self | +| scopes.rb:80:13:80:17 | value | scopes.rb:80:13:80:17 | value | +| scopes.rb:84:3:88:5 | self (foo) | scopes.rb:84:3:88:5 | self | +| scopes.rb:84:11:84:13 | msg | scopes.rb:84:11:84:13 | msg | | ssa.rb:1:1:16:3 | self (m) | ssa.rb:1:1:16:3 | self | | ssa.rb:1:7:1:7 | b | ssa.rb:1:7:1:7 | b | | ssa.rb:2:3:2:3 | i | ssa.rb:2:3:2:3 | i | @@ -267,20 +273,20 @@ read | parameters.rb:59:20:59:20 | a | parameters.rb:59:20:59:20 | a | parameters.rb:60:11:60:11 | a | | parameters.rb:59:23:59:23 | b | parameters.rb:59:23:59:23 | b | parameters.rb:60:16:60:16 | b | | parameters.rb:59:25:59:25 | c | parameters.rb:59:25:59:25 | c | parameters.rb:60:21:60:21 | c | -| scopes.rb:1:1:73:3 | self (scopes.rb) | scopes.rb:1:1:73:3 | self | scopes.rb:8:1:8:6 | self | -| scopes.rb:2:9:6:3 | self | scopes.rb:1:1:73:3 | self | scopes.rb:3:4:3:9 | self | -| scopes.rb:2:9:6:3 | self | scopes.rb:1:1:73:3 | self | scopes.rb:3:9:3:9 | self | -| scopes.rb:2:9:6:3 | self | scopes.rb:1:1:73:3 | self | scopes.rb:5:4:5:9 | self | +| scopes.rb:1:1:89:4 | self (scopes.rb) | scopes.rb:1:1:89:4 | self | scopes.rb:8:1:8:6 | self | +| scopes.rb:2:9:6:3 | self | scopes.rb:1:1:89:4 | self | scopes.rb:3:4:3:9 | self | +| scopes.rb:2:9:6:3 | self | scopes.rb:1:1:89:4 | self | scopes.rb:3:9:3:9 | self | +| scopes.rb:2:9:6:3 | self | scopes.rb:1:1:89:4 | self | scopes.rb:5:4:5:9 | self | | scopes.rb:4:4:4:4 | a | scopes.rb:4:4:4:4 | a | scopes.rb:5:9:5:9 | a | | scopes.rb:7:1:7:1 | a | scopes.rb:7:1:7:1 | a | scopes.rb:8:6:8:6 | a | | scopes.rb:9:9:18:3 | a | scopes.rb:7:1:7:1 | a | scopes.rb:10:9:10:9 | a | | scopes.rb:9:9:18:3 | a | scopes.rb:7:1:7:1 | a | scopes.rb:11:4:11:4 | a | -| scopes.rb:9:9:18:3 | self | scopes.rb:1:1:73:3 | self | scopes.rb:10:4:10:9 | self | -| scopes.rb:9:9:18:3 | self | scopes.rb:1:1:73:3 | self | scopes.rb:12:4:12:9 | self | -| scopes.rb:9:9:18:3 | self | scopes.rb:1:1:73:3 | self | scopes.rb:14:4:14:9 | self | -| scopes.rb:9:9:18:3 | self | scopes.rb:1:1:73:3 | self | scopes.rb:15:4:15:9 | self | -| scopes.rb:9:9:18:3 | self | scopes.rb:1:1:73:3 | self | scopes.rb:16:4:16:9 | self | -| scopes.rb:9:9:18:3 | self | scopes.rb:1:1:73:3 | self | scopes.rb:17:4:17:9 | self | +| scopes.rb:9:9:18:3 | self | scopes.rb:1:1:89:4 | self | scopes.rb:10:4:10:9 | self | +| scopes.rb:9:9:18:3 | self | scopes.rb:1:1:89:4 | self | scopes.rb:12:4:12:9 | self | +| scopes.rb:9:9:18:3 | self | scopes.rb:1:1:89:4 | self | scopes.rb:14:4:14:9 | self | +| scopes.rb:9:9:18:3 | self | scopes.rb:1:1:89:4 | self | scopes.rb:15:4:15:9 | self | +| scopes.rb:9:9:18:3 | self | scopes.rb:1:1:89:4 | self | scopes.rb:16:4:16:9 | self | +| scopes.rb:9:9:18:3 | self | scopes.rb:1:1:89:4 | self | scopes.rb:17:4:17:9 | self | | scopes.rb:11:4:11:4 | a | scopes.rb:7:1:7:1 | a | scopes.rb:12:9:12:9 | a | | scopes.rb:13:4:13:4 | a | scopes.rb:7:1:7:1 | a | scopes.rb:14:9:14:9 | a | | scopes.rb:13:7:13:7 | b | scopes.rb:13:7:13:7 | b | scopes.rb:15:9:15:9 | b | @@ -311,6 +317,12 @@ read | scopes.rb:68:3:68:4 | xs | scopes.rb:68:3:68:4 | xs | scopes.rb:69:3:69:4 | xs | | scopes.rb:69:11:71:5 | self | scopes.rb:66:1:73:3 | self | scopes.rb:70:5:70:10 | self | | scopes.rb:69:15:69:15 | x | scopes.rb:69:15:69:15 | x | scopes.rb:70:10:70:10 | x | +| scopes.rb:76:3:78:5 | self (name) | scopes.rb:76:3:78:5 | self | scopes.rb:77:5:77:9 | self | +| scopes.rb:80:3:82:5 | self (name=) | scopes.rb:80:3:82:5 | self | scopes.rb:81:5:81:9 | self | +| scopes.rb:80:13:80:17 | value | scopes.rb:80:13:80:17 | value | scopes.rb:81:13:81:17 | value | +| scopes.rb:84:3:88:5 | self (foo) | scopes.rb:84:3:88:5 | self | scopes.rb:85:5:85:13 | self | +| scopes.rb:84:3:88:5 | self (foo) | scopes.rb:84:3:88:5 | self | scopes.rb:86:13:86:16 | self | +| scopes.rb:84:11:84:13 | msg | scopes.rb:84:11:84:13 | msg | scopes.rb:85:11:85:13 | msg | | ssa.rb:1:1:16:3 | self (m) | ssa.rb:1:1:16:3 | self | ssa.rb:3:3:3:8 | self | | ssa.rb:1:1:16:3 | self (m) | ssa.rb:1:1:16:3 | self | ssa.rb:4:3:4:12 | self | | ssa.rb:1:1:16:3 | self (m) | ssa.rb:1:1:16:3 | self | ssa.rb:7:5:7:10 | self | @@ -460,12 +472,12 @@ firstRead | parameters.rb:59:20:59:20 | a | parameters.rb:59:20:59:20 | a | parameters.rb:60:11:60:11 | a | | parameters.rb:59:23:59:23 | b | parameters.rb:59:23:59:23 | b | parameters.rb:60:16:60:16 | b | | parameters.rb:59:25:59:25 | c | parameters.rb:59:25:59:25 | c | parameters.rb:60:21:60:21 | c | -| scopes.rb:1:1:73:3 | self (scopes.rb) | scopes.rb:1:1:73:3 | self | scopes.rb:8:1:8:6 | self | -| scopes.rb:2:9:6:3 | self | scopes.rb:1:1:73:3 | self | scopes.rb:3:4:3:9 | self | +| scopes.rb:1:1:89:4 | self (scopes.rb) | scopes.rb:1:1:89:4 | self | scopes.rb:8:1:8:6 | self | +| scopes.rb:2:9:6:3 | self | scopes.rb:1:1:89:4 | self | scopes.rb:3:4:3:9 | self | | scopes.rb:4:4:4:4 | a | scopes.rb:4:4:4:4 | a | scopes.rb:5:9:5:9 | a | | scopes.rb:7:1:7:1 | a | scopes.rb:7:1:7:1 | a | scopes.rb:8:6:8:6 | a | | scopes.rb:9:9:18:3 | a | scopes.rb:7:1:7:1 | a | scopes.rb:10:9:10:9 | a | -| scopes.rb:9:9:18:3 | self | scopes.rb:1:1:73:3 | self | scopes.rb:10:4:10:9 | self | +| scopes.rb:9:9:18:3 | self | scopes.rb:1:1:89:4 | self | scopes.rb:10:4:10:9 | self | | scopes.rb:11:4:11:4 | a | scopes.rb:7:1:7:1 | a | scopes.rb:12:9:12:9 | a | | scopes.rb:13:4:13:4 | a | scopes.rb:7:1:7:1 | a | scopes.rb:14:9:14:9 | a | | scopes.rb:13:7:13:7 | b | scopes.rb:13:7:13:7 | b | scopes.rb:15:9:15:9 | b | @@ -485,6 +497,11 @@ firstRead | scopes.rb:68:3:68:4 | xs | scopes.rb:68:3:68:4 | xs | scopes.rb:69:3:69:4 | xs | | scopes.rb:69:11:71:5 | self | scopes.rb:66:1:73:3 | self | scopes.rb:70:5:70:10 | self | | scopes.rb:69:15:69:15 | x | scopes.rb:69:15:69:15 | x | scopes.rb:70:10:70:10 | x | +| scopes.rb:76:3:78:5 | self (name) | scopes.rb:76:3:78:5 | self | scopes.rb:77:5:77:9 | self | +| scopes.rb:80:3:82:5 | self (name=) | scopes.rb:80:3:82:5 | self | scopes.rb:81:5:81:9 | self | +| scopes.rb:80:13:80:17 | value | scopes.rb:80:13:80:17 | value | scopes.rb:81:13:81:17 | value | +| scopes.rb:84:3:88:5 | self (foo) | scopes.rb:84:3:88:5 | self | scopes.rb:85:5:85:13 | self | +| scopes.rb:84:11:84:13 | msg | scopes.rb:84:11:84:13 | msg | scopes.rb:85:11:85:13 | msg | | ssa.rb:1:1:16:3 | self (m) | ssa.rb:1:1:16:3 | self | ssa.rb:3:3:3:8 | self | | ssa.rb:1:7:1:7 | b | ssa.rb:1:7:1:7 | b | ssa.rb:5:6:5:6 | b | | ssa.rb:2:3:2:3 | i | ssa.rb:2:3:2:3 | i | ssa.rb:3:8:3:8 | i | @@ -557,14 +574,14 @@ adjacentReads | parameters.rb:25:1:28:3 | self (opt_param) | parameters.rb:25:1:28:3 | self | parameters.rb:26:3:26:11 | self | parameters.rb:27:3:27:11 | self | | parameters.rb:25:15:25:18 | name | parameters.rb:25:15:25:18 | name | parameters.rb:25:40:25:43 | name | parameters.rb:26:8:26:11 | name | | parameters.rb:54:9:57:3 | self | parameters.rb:1:1:62:1 | self | parameters.rb:55:4:55:9 | self | parameters.rb:56:4:56:9 | self | -| scopes.rb:2:9:6:3 | self | scopes.rb:1:1:73:3 | self | scopes.rb:3:4:3:9 | self | scopes.rb:3:9:3:9 | self | -| scopes.rb:2:9:6:3 | self | scopes.rb:1:1:73:3 | self | scopes.rb:3:9:3:9 | self | scopes.rb:5:4:5:9 | self | +| scopes.rb:2:9:6:3 | self | scopes.rb:1:1:89:4 | self | scopes.rb:3:4:3:9 | self | scopes.rb:3:9:3:9 | self | +| scopes.rb:2:9:6:3 | self | scopes.rb:1:1:89:4 | self | scopes.rb:3:9:3:9 | self | scopes.rb:5:4:5:9 | self | | scopes.rb:9:9:18:3 | a | scopes.rb:7:1:7:1 | a | scopes.rb:10:9:10:9 | a | scopes.rb:11:4:11:4 | a | -| scopes.rb:9:9:18:3 | self | scopes.rb:1:1:73:3 | self | scopes.rb:10:4:10:9 | self | scopes.rb:12:4:12:9 | self | -| scopes.rb:9:9:18:3 | self | scopes.rb:1:1:73:3 | self | scopes.rb:12:4:12:9 | self | scopes.rb:14:4:14:9 | self | -| scopes.rb:9:9:18:3 | self | scopes.rb:1:1:73:3 | self | scopes.rb:14:4:14:9 | self | scopes.rb:15:4:15:9 | self | -| scopes.rb:9:9:18:3 | self | scopes.rb:1:1:73:3 | self | scopes.rb:15:4:15:9 | self | scopes.rb:16:4:16:9 | self | -| scopes.rb:9:9:18:3 | self | scopes.rb:1:1:73:3 | self | scopes.rb:16:4:16:9 | self | scopes.rb:17:4:17:9 | self | +| scopes.rb:9:9:18:3 | self | scopes.rb:1:1:89:4 | self | scopes.rb:10:4:10:9 | self | scopes.rb:12:4:12:9 | self | +| scopes.rb:9:9:18:3 | self | scopes.rb:1:1:89:4 | self | scopes.rb:12:4:12:9 | self | scopes.rb:14:4:14:9 | self | +| scopes.rb:9:9:18:3 | self | scopes.rb:1:1:89:4 | self | scopes.rb:14:4:14:9 | self | scopes.rb:15:4:15:9 | self | +| scopes.rb:9:9:18:3 | self | scopes.rb:1:1:89:4 | self | scopes.rb:15:4:15:9 | self | scopes.rb:16:4:16:9 | self | +| scopes.rb:9:9:18:3 | self | scopes.rb:1:1:89:4 | self | scopes.rb:16:4:16:9 | self | scopes.rb:17:4:17:9 | self | | scopes.rb:13:10:13:15 | __synth__2__1 | scopes.rb:13:10:13:15 | __synth__2__1 | scopes.rb:13:11:13:11 | __synth__2__1 | scopes.rb:13:14:13:14 | __synth__2__1 | | scopes.rb:13:19:13:32 | __synth__3 | scopes.rb:13:4:13:32 | __synth__3 | scopes.rb:13:4:13:4 | __synth__3 | scopes.rb:13:7:13:7 | __synth__3 | | scopes.rb:13:19:13:32 | __synth__3 | scopes.rb:13:4:13:32 | __synth__3 | scopes.rb:13:7:13:7 | __synth__3 | scopes.rb:13:10:13:15 | __synth__3 | @@ -576,6 +593,7 @@ adjacentReads | scopes.rb:51:1:64:3 | self (ExceptionVariable) | scopes.rb:51:1:64:3 | self | scopes.rb:59:5:59:21 | self | scopes.rb:61:5:61:10 | self | | scopes.rb:51:1:64:3 | self (ExceptionVariable) | scopes.rb:51:1:64:3 | self | scopes.rb:61:5:61:10 | self | scopes.rb:63:3:63:8 | self | | scopes.rb:60:25:60:25 | x | scopes.rb:55:3:55:3 | x | scopes.rb:61:10:61:10 | x | scopes.rb:63:8:63:8 | x | +| scopes.rb:84:3:88:5 | self (foo) | scopes.rb:84:3:88:5 | self | scopes.rb:85:5:85:13 | self | scopes.rb:86:13:86:16 | self | | ssa.rb:1:1:16:3 | self (m) | ssa.rb:1:1:16:3 | self | ssa.rb:3:3:3:8 | self | ssa.rb:4:3:4:12 | self | | ssa.rb:1:1:16:3 | self (m) | ssa.rb:1:1:16:3 | self | ssa.rb:4:3:4:12 | self | ssa.rb:7:5:7:10 | self | | ssa.rb:1:1:16:3 | self (m) | ssa.rb:1:1:16:3 | self | ssa.rb:4:3:4:12 | self | ssa.rb:11:5:11:10 | self | diff --git a/ruby/ql/test/library-tests/variables/varaccess.expected b/ruby/ql/test/library-tests/variables/varaccess.expected index 56113f13e35..22f37fda64c 100644 --- a/ruby/ql/test/library-tests/variables/varaccess.expected +++ b/ruby/ql/test/library-tests/variables/varaccess.expected @@ -155,43 +155,43 @@ variableAccess | parameters.rb:60:16:60:16 | b | parameters.rb:59:23:59:23 | b | parameters.rb:59:1:61:3 | tuples_nested | | parameters.rb:60:21:60:21 | c | parameters.rb:59:25:59:25 | c | parameters.rb:59:1:61:3 | tuples_nested | | scopes.rb:2:14:2:14 | x | scopes.rb:2:14:2:14 | x | scopes.rb:2:9:6:3 | do ... end | -| scopes.rb:3:4:3:9 | self | scopes.rb:1:1:73:3 | self | scopes.rb:1:1:73:3 | scopes.rb | -| scopes.rb:3:9:3:9 | self | scopes.rb:1:1:73:3 | self | scopes.rb:1:1:73:3 | scopes.rb | +| scopes.rb:3:4:3:9 | self | scopes.rb:1:1:89:4 | self | scopes.rb:1:1:89:4 | scopes.rb | +| scopes.rb:3:9:3:9 | self | scopes.rb:1:1:89:4 | self | scopes.rb:1:1:89:4 | scopes.rb | | scopes.rb:4:4:4:4 | a | scopes.rb:4:4:4:4 | a | scopes.rb:2:9:6:3 | do ... end | -| scopes.rb:5:4:5:9 | self | scopes.rb:1:1:73:3 | self | scopes.rb:1:1:73:3 | scopes.rb | +| scopes.rb:5:4:5:9 | self | scopes.rb:1:1:89:4 | self | scopes.rb:1:1:89:4 | scopes.rb | | scopes.rb:5:9:5:9 | a | scopes.rb:4:4:4:4 | a | scopes.rb:2:9:6:3 | do ... end | -| scopes.rb:7:1:7:1 | a | scopes.rb:7:1:7:1 | a | scopes.rb:1:1:73:3 | scopes.rb | -| scopes.rb:8:1:8:6 | self | scopes.rb:1:1:73:3 | self | scopes.rb:1:1:73:3 | scopes.rb | -| scopes.rb:8:6:8:6 | a | scopes.rb:7:1:7:1 | a | scopes.rb:1:1:73:3 | scopes.rb | +| scopes.rb:7:1:7:1 | a | scopes.rb:7:1:7:1 | a | scopes.rb:1:1:89:4 | scopes.rb | +| scopes.rb:8:1:8:6 | self | scopes.rb:1:1:89:4 | self | scopes.rb:1:1:89:4 | scopes.rb | +| scopes.rb:8:6:8:6 | a | scopes.rb:7:1:7:1 | a | scopes.rb:1:1:89:4 | scopes.rb | | scopes.rb:9:14:9:14 | x | scopes.rb:9:14:9:14 | x | scopes.rb:9:9:18:3 | do ... end | -| scopes.rb:10:4:10:9 | self | scopes.rb:1:1:73:3 | self | scopes.rb:1:1:73:3 | scopes.rb | -| scopes.rb:10:9:10:9 | a | scopes.rb:7:1:7:1 | a | scopes.rb:1:1:73:3 | scopes.rb | -| scopes.rb:11:4:11:4 | a | scopes.rb:7:1:7:1 | a | scopes.rb:1:1:73:3 | scopes.rb | -| scopes.rb:11:4:11:4 | a | scopes.rb:7:1:7:1 | a | scopes.rb:1:1:73:3 | scopes.rb | -| scopes.rb:12:4:12:9 | self | scopes.rb:1:1:73:3 | self | scopes.rb:1:1:73:3 | scopes.rb | -| scopes.rb:12:9:12:9 | a | scopes.rb:7:1:7:1 | a | scopes.rb:1:1:73:3 | scopes.rb | -| scopes.rb:13:4:13:4 | a | scopes.rb:7:1:7:1 | a | scopes.rb:1:1:73:3 | scopes.rb | +| scopes.rb:10:4:10:9 | self | scopes.rb:1:1:89:4 | self | scopes.rb:1:1:89:4 | scopes.rb | +| scopes.rb:10:9:10:9 | a | scopes.rb:7:1:7:1 | a | scopes.rb:1:1:89:4 | scopes.rb | +| scopes.rb:11:4:11:4 | a | scopes.rb:7:1:7:1 | a | scopes.rb:1:1:89:4 | scopes.rb | +| scopes.rb:11:4:11:4 | a | scopes.rb:7:1:7:1 | a | scopes.rb:1:1:89:4 | scopes.rb | +| scopes.rb:12:4:12:9 | self | scopes.rb:1:1:89:4 | self | scopes.rb:1:1:89:4 | scopes.rb | +| scopes.rb:12:9:12:9 | a | scopes.rb:7:1:7:1 | a | scopes.rb:1:1:89:4 | scopes.rb | +| scopes.rb:13:4:13:4 | a | scopes.rb:7:1:7:1 | a | scopes.rb:1:1:89:4 | scopes.rb | | scopes.rb:13:7:13:7 | b | scopes.rb:13:7:13:7 | b | scopes.rb:9:9:18:3 | do ... end | | scopes.rb:13:11:13:11 | c | scopes.rb:13:11:13:11 | c | scopes.rb:9:9:18:3 | do ... end | | scopes.rb:13:14:13:14 | d | scopes.rb:13:14:13:14 | d | scopes.rb:9:9:18:3 | do ... end | -| scopes.rb:14:4:14:9 | self | scopes.rb:1:1:73:3 | self | scopes.rb:1:1:73:3 | scopes.rb | -| scopes.rb:14:9:14:9 | a | scopes.rb:7:1:7:1 | a | scopes.rb:1:1:73:3 | scopes.rb | -| scopes.rb:15:4:15:9 | self | scopes.rb:1:1:73:3 | self | scopes.rb:1:1:73:3 | scopes.rb | +| scopes.rb:14:4:14:9 | self | scopes.rb:1:1:89:4 | self | scopes.rb:1:1:89:4 | scopes.rb | +| scopes.rb:14:9:14:9 | a | scopes.rb:7:1:7:1 | a | scopes.rb:1:1:89:4 | scopes.rb | +| scopes.rb:15:4:15:9 | self | scopes.rb:1:1:89:4 | self | scopes.rb:1:1:89:4 | scopes.rb | | scopes.rb:15:9:15:9 | b | scopes.rb:13:7:13:7 | b | scopes.rb:9:9:18:3 | do ... end | -| scopes.rb:16:4:16:9 | self | scopes.rb:1:1:73:3 | self | scopes.rb:1:1:73:3 | scopes.rb | +| scopes.rb:16:4:16:9 | self | scopes.rb:1:1:89:4 | self | scopes.rb:1:1:89:4 | scopes.rb | | scopes.rb:16:9:16:9 | c | scopes.rb:13:11:13:11 | c | scopes.rb:9:9:18:3 | do ... end | -| scopes.rb:17:4:17:9 | self | scopes.rb:1:1:73:3 | self | scopes.rb:1:1:73:3 | scopes.rb | +| scopes.rb:17:4:17:9 | self | scopes.rb:1:1:89:4 | self | scopes.rb:1:1:89:4 | scopes.rb | | scopes.rb:17:9:17:9 | d | scopes.rb:13:14:13:14 | d | scopes.rb:9:9:18:3 | do ... end | -| scopes.rb:24:1:24:6 | script | scopes.rb:24:1:24:6 | script | scopes.rb:1:1:73:3 | scopes.rb | -| scopes.rb:27:1:27:1 | x | scopes.rb:27:1:27:1 | x | scopes.rb:1:1:73:3 | scopes.rb | -| scopes.rb:28:8:28:8 | x | scopes.rb:27:1:27:1 | x | scopes.rb:1:1:73:3 | scopes.rb | +| scopes.rb:24:1:24:6 | script | scopes.rb:24:1:24:6 | script | scopes.rb:1:1:89:4 | scopes.rb | +| scopes.rb:27:1:27:1 | x | scopes.rb:27:1:27:1 | x | scopes.rb:1:1:89:4 | scopes.rb | +| scopes.rb:28:8:28:8 | x | scopes.rb:27:1:27:1 | x | scopes.rb:1:1:89:4 | scopes.rb | | scopes.rb:29:3:29:3 | x | scopes.rb:29:3:29:3 | x | scopes.rb:28:1:30:3 | B | -| scopes.rb:31:10:31:10 | x | scopes.rb:27:1:27:1 | x | scopes.rb:1:1:73:3 | scopes.rb | +| scopes.rb:31:10:31:10 | x | scopes.rb:27:1:27:1 | x | scopes.rb:1:1:89:4 | scopes.rb | | scopes.rb:32:3:32:3 | x | scopes.rb:32:3:32:3 | x | scopes.rb:31:1:33:3 | class << ... | -| scopes.rb:34:7:34:7 | x | scopes.rb:27:1:27:1 | x | scopes.rb:1:1:73:3 | scopes.rb | -| scopes.rb:34:14:34:14 | x | scopes.rb:27:1:27:1 | x | scopes.rb:1:1:73:3 | scopes.rb | +| scopes.rb:34:7:34:7 | x | scopes.rb:27:1:27:1 | x | scopes.rb:1:1:89:4 | scopes.rb | +| scopes.rb:34:14:34:14 | x | scopes.rb:27:1:27:1 | x | scopes.rb:1:1:89:4 | scopes.rb | | scopes.rb:35:3:35:3 | x | scopes.rb:35:3:35:3 | x | scopes.rb:34:1:36:3 | C | -| scopes.rb:37:5:37:5 | x | scopes.rb:27:1:27:1 | x | scopes.rb:1:1:73:3 | scopes.rb | +| scopes.rb:37:5:37:5 | x | scopes.rb:27:1:27:1 | x | scopes.rb:1:1:89:4 | scopes.rb | | scopes.rb:38:3:38:3 | x | scopes.rb:38:3:38:3 | x | scopes.rb:37:1:39:3 | foo | | scopes.rb:42:2:42:4 | var | scopes.rb:42:2:42:4 | var | scopes.rb:41:1:49:3 | M | | scopes.rb:43:2:43:4 | foo | scopes.rb:43:2:43:4 | foo | scopes.rb:41:1:49:3 | M | @@ -216,6 +216,16 @@ variableAccess | scopes.rb:70:10:70:10 | x | scopes.rb:69:15:69:15 | x | scopes.rb:69:11:71:5 | do ... end | | scopes.rb:72:3:72:8 | self | scopes.rb:66:1:73:3 | self | scopes.rb:66:1:73:3 | ParameterShadowing | | scopes.rb:72:8:72:8 | x | scopes.rb:67:3:67:3 | x | scopes.rb:66:1:73:3 | ParameterShadowing | +| scopes.rb:77:5:77:9 | @name | scopes.rb:77:5:77:9 | @name | scopes.rb:75:1:89:3 | RescueSetter | +| scopes.rb:77:5:77:9 | self | scopes.rb:76:3:78:5 | self | scopes.rb:76:3:78:5 | name | +| scopes.rb:80:13:80:17 | value | scopes.rb:80:13:80:17 | value | scopes.rb:80:3:82:5 | name= | +| scopes.rb:81:5:81:9 | @name | scopes.rb:77:5:77:9 | @name | scopes.rb:75:1:89:3 | RescueSetter | +| scopes.rb:81:5:81:9 | self | scopes.rb:80:3:82:5 | self | scopes.rb:80:3:82:5 | name= | +| scopes.rb:81:13:81:17 | value | scopes.rb:80:13:80:17 | value | scopes.rb:80:3:82:5 | name= | +| scopes.rb:84:11:84:13 | msg | scopes.rb:84:11:84:13 | msg | scopes.rb:84:3:88:5 | foo | +| scopes.rb:85:5:85:13 | self | scopes.rb:84:3:88:5 | self | scopes.rb:84:3:88:5 | foo | +| scopes.rb:85:11:85:13 | msg | scopes.rb:84:11:84:13 | msg | scopes.rb:84:3:88:5 | foo | +| scopes.rb:86:13:86:16 | self | scopes.rb:84:3:88:5 | self | scopes.rb:84:3:88:5 | foo | | ssa.rb:1:7:1:7 | b | ssa.rb:1:7:1:7 | b | ssa.rb:1:1:16:3 | m | | ssa.rb:2:3:2:3 | i | ssa.rb:2:3:2:3 | i | ssa.rb:1:1:16:3 | m | | ssa.rb:3:3:3:8 | self | ssa.rb:1:1:16:3 | self | ssa.rb:1:1:16:3 | m | @@ -370,6 +380,7 @@ explicitWrite | scopes.rb:55:3:55:3 | x | scopes.rb:55:3:55:7 | ... = ... | | scopes.rb:67:3:67:3 | x | scopes.rb:67:3:67:7 | ... = ... | | scopes.rb:68:3:68:4 | xs | scopes.rb:68:3:68:16 | ... = ... | +| scopes.rb:81:5:81:9 | @name | scopes.rb:81:5:81:17 | ... = ... | | ssa.rb:2:3:2:3 | i | ssa.rb:2:3:2:7 | ... = ... | | ssa.rb:6:5:6:5 | i | ssa.rb:6:5:6:9 | ... = ... | | ssa.rb:10:5:10:5 | i | ssa.rb:10:5:10:9 | ... = ... | @@ -422,6 +433,8 @@ implicitWrite | scopes.rb:9:14:9:14 | x | | scopes.rb:60:25:60:25 | x | | scopes.rb:69:15:69:15 | x | +| scopes.rb:80:13:80:17 | value | +| scopes.rb:84:11:84:13 | msg | | ssa.rb:1:7:1:7 | b | | ssa.rb:18:8:18:8 | x | | ssa.rb:25:8:25:15 | elements | @@ -584,6 +597,13 @@ readAccess | scopes.rb:70:10:70:10 | x | | scopes.rb:72:3:72:8 | self | | scopes.rb:72:8:72:8 | x | +| scopes.rb:77:5:77:9 | @name | +| scopes.rb:77:5:77:9 | self | +| scopes.rb:81:5:81:9 | self | +| scopes.rb:81:13:81:17 | value | +| scopes.rb:85:5:85:13 | self | +| scopes.rb:85:11:85:13 | msg | +| scopes.rb:86:13:86:16 | self | | ssa.rb:3:3:3:8 | self | | ssa.rb:3:8:3:8 | i | | ssa.rb:4:3:4:12 | self | diff --git a/ruby/ql/test/library-tests/variables/variable.expected b/ruby/ql/test/library-tests/variables/variable.expected index b0e23fb2045..32e4c87bb93 100644 --- a/ruby/ql/test/library-tests/variables/variable.expected +++ b/ruby/ql/test/library-tests/variables/variable.expected @@ -94,7 +94,7 @@ | parameters.rb:59:23:59:23 | b | | parameters.rb:59:25:59:25 | c | | scopes.rb:1:1:1:15 | self | -| scopes.rb:1:1:73:3 | self | +| scopes.rb:1:1:89:4 | self | | scopes.rb:2:14:2:14 | x | | scopes.rb:4:4:4:4 | a | | scopes.rb:7:1:7:1 | a | @@ -131,6 +131,13 @@ | scopes.rb:67:3:67:3 | x | | scopes.rb:68:3:68:4 | xs | | scopes.rb:69:15:69:15 | x | +| scopes.rb:75:1:89:3 | self | +| scopes.rb:76:3:78:5 | self | +| scopes.rb:77:5:77:9 | @name | +| scopes.rb:80:3:82:5 | self | +| scopes.rb:80:13:80:17 | value | +| scopes.rb:84:3:88:5 | self | +| scopes.rb:84:11:84:13 | msg | | ssa.rb:1:1:16:3 | self | | ssa.rb:1:1:103:3 | self | | ssa.rb:1:7:1:7 | b | diff --git a/ruby/ql/test/library-tests/variables/varscopes.expected b/ruby/ql/test/library-tests/variables/varscopes.expected index 958be320a5d..6e9874ebeb7 100644 --- a/ruby/ql/test/library-tests/variables/varscopes.expected +++ b/ruby/ql/test/library-tests/variables/varscopes.expected @@ -47,7 +47,7 @@ | parameters.rb:54:9:57:3 | do ... end | | parameters.rb:59:1:61:3 | tuples_nested | | scopes.rb:1:1:1:15 | a | -| scopes.rb:1:1:73:3 | scopes.rb | +| scopes.rb:1:1:89:4 | scopes.rb | | scopes.rb:2:9:6:3 | do ... end | | scopes.rb:9:9:18:3 | do ... end | | scopes.rb:26:1:26:12 | A | @@ -60,6 +60,10 @@ | scopes.rb:52:3:53:5 | MyException | | scopes.rb:66:1:73:3 | ParameterShadowing | | scopes.rb:69:11:71:5 | do ... end | +| scopes.rb:75:1:89:3 | RescueSetter | +| scopes.rb:76:3:78:5 | name | +| scopes.rb:80:3:82:5 | name= | +| scopes.rb:84:3:88:5 | foo | | ssa.rb:1:1:16:3 | m | | ssa.rb:1:1:103:3 | ssa.rb | | ssa.rb:18:1:23:3 | m1 | diff --git a/ruby/ql/test/query-tests/experimental/CWE-522-DecompressionBombs/DecompressionBombs.qlref b/ruby/ql/test/query-tests/experimental/CWE-522-DecompressionBombs/DecompressionBombs.qlref index c24a4cc9678..e65789fc0d9 100644 --- a/ruby/ql/test/query-tests/experimental/CWE-522-DecompressionBombs/DecompressionBombs.qlref +++ b/ruby/ql/test/query-tests/experimental/CWE-522-DecompressionBombs/DecompressionBombs.qlref @@ -1 +1,2 @@ -experimental/CWE-522-DecompressionBombs/DecompressionBombs.ql \ No newline at end of file +query: experimental/CWE-522-DecompressionBombs/DecompressionBombs.ql +postprocess: utils/test/InlineExpectationsTestQuery.ql diff --git a/ruby/ql/test/query-tests/experimental/CWE-522-DecompressionBombs/gzipBombs.rb b/ruby/ql/test/query-tests/experimental/CWE-522-DecompressionBombs/gzipBombs.rb index bf9bb7b329d..1a7636809b1 100644 --- a/ruby/ql/test/query-tests/experimental/CWE-522-DecompressionBombs/gzipBombs.rb +++ b/ruby/ql/test/query-tests/experimental/CWE-522-DecompressionBombs/gzipBombs.rb @@ -1,27 +1,27 @@ require 'zlib' class TestController < ActionController::Base - gzip_path = params[:path] + gzip_path = params[:path] # $ Source - Zlib::GzipReader.open(gzip_path).read + Zlib::GzipReader.open(gzip_path).read # $ Alert Zlib::GzipReader.open(gzip_path) do |uncompressedfile| puts uncompressedfile.read - end + end # $ Alert Zlib::GzipReader.open(gzip_path) do |uncompressedfile| uncompressedfile.each do |entry| puts entry end - end - uncompressedfile = Zlib::GzipReader.open(gzip_path) + end # $ Alert + uncompressedfile = Zlib::GzipReader.open(gzip_path) # $ Alert uncompressedfile.each do |entry| puts entry end - Zlib::GzipReader.new(File.open(gzip_path, 'rb')).read - Zlib::GzipReader.new(File.open(gzip_path, 'rb')).each do |entry| + Zlib::GzipReader.new(File.open(gzip_path, 'rb')).read # $ Alert + Zlib::GzipReader.new(File.open(gzip_path, 'rb')).each do |entry| # $ Alert puts entry end - Zlib::GzipReader.zcat(open(gzip_path)) + Zlib::GzipReader.zcat(open(gzip_path)) # $ Alert end diff --git a/ruby/ql/test/query-tests/experimental/CWE-522-DecompressionBombs/zipBombs.rb b/ruby/ql/test/query-tests/experimental/CWE-522-DecompressionBombs/zipBombs.rb index 5aab5ce6382..9d0d047b035 100644 --- a/ruby/ql/test/query-tests/experimental/CWE-522-DecompressionBombs/zipBombs.rb +++ b/ruby/ql/test/query-tests/experimental/CWE-522-DecompressionBombs/zipBombs.rb @@ -1,21 +1,21 @@ require 'zip' class TestController < ActionController::Base - zipfile_path = params[:path] + zipfile_path = params[:path] # $ Source Zip::InputStream.open(zipfile_path) do |input| while (entry = input.get_next_entry) puts :file_name, entry.name input end - end + end # $ Alert Zip::InputStream.open(zipfile_path) do |input| input.read - end - input = Zip::InputStream.open(zipfile_path) + end # $ Alert + input = Zip::InputStream.open(zipfile_path) # $ Alert - Zip::File.open(zipfile_path).read "10GB" - Zip::File.open(zipfile_path).extract "10GB", "./" + Zip::File.open(zipfile_path).read "10GB" # $ Alert + Zip::File.open(zipfile_path).extract "10GB", "./" # $ Alert Zip::File.open(zipfile_path) do |zip_file| # Handle entries one by one @@ -25,33 +25,33 @@ class TestController < ActionController::Base # Extract to file or directory based on name in the archive entry.extract # Read into memory - entry.get_input_stream.read + entry.get_input_stream.read # $ Alert end end zip_file = Zip::File.open(zipfile_path) zip_file.each do |entry| - entry.extract - entry.get_input_stream.read + entry.extract # $ Alert + entry.get_input_stream.read # $ Alert end # Find specific entry Zip::File.open(zipfile_path) do |zip_file| zip_file.glob('*.xml').each do |entry| - zip_file.read(entry.name) - entry.extract + zip_file.read(entry.name) # $ Alert + entry.extract # $ Alert end entry = zip_file.glob('*.csv').first raise 'File too large when extracted' if entry.size > MAX_SIZE - puts entry.get_input_stream.read + puts entry.get_input_stream.read # $ Alert end zip_file = Zip::File.open(zipfile_path) entry = zip_file.glob('*.csv') - puts entry.get_input_stream.read + puts entry.get_input_stream.read # $ Alert zip_file = Zip::File.open(zipfile_path) zip_file.glob('*') do |entry| - entry.get_input_stream.read + entry.get_input_stream.read # $ Alert end end diff --git a/ruby/ql/test/query-tests/experimental/ImproperLdapAuth/ImproperLdapAuth.qlref b/ruby/ql/test/query-tests/experimental/ImproperLdapAuth/ImproperLdapAuth.qlref index 65f60a22b78..42e36ad38a8 100644 --- a/ruby/ql/test/query-tests/experimental/ImproperLdapAuth/ImproperLdapAuth.qlref +++ b/ruby/ql/test/query-tests/experimental/ImproperLdapAuth/ImproperLdapAuth.qlref @@ -1 +1,2 @@ -experimental/ldap-improper-auth/ImproperLdapAuth.ql \ No newline at end of file +query: experimental/ldap-improper-auth/ImproperLdapAuth.ql +postprocess: utils/test/InlineExpectationsTestQuery.ql diff --git a/ruby/ql/test/query-tests/experimental/ImproperLdapAuth/ImproperLdapAuth.rb b/ruby/ql/test/query-tests/experimental/ImproperLdapAuth/ImproperLdapAuth.rb index 2705158563e..19acc8a841a 100644 --- a/ruby/ql/test/query-tests/experimental/ImproperLdapAuth/ImproperLdapAuth.rb +++ b/ruby/ql/test/query-tests/experimental/ImproperLdapAuth/ImproperLdapAuth.rb @@ -2,7 +2,7 @@ class FooController < ActionController::Base def some_request_handler # A string tainted by user input is used directly as password # (i.e a remote flow source) - pass = params[:pass] + pass = params[:pass] # $ Source # BAD: user input is not sanitized ldap = Net::LDAP.new( @@ -12,7 +12,7 @@ class FooController < ActionController::Base auth: { method: :simple, username: 'uid=admin,dc=example,dc=com', - password: pass + password: pass # $ Alert } ) ldap.bind @@ -21,14 +21,14 @@ class FooController < ActionController::Base def some_request_handler # A string tainted by user input is used directly as password # (i.e a remote flow source) - pass = params[:pass] + pass = params[:pass] # $ Source # BAD: user input is not sanitized ldap = Net::LDAP.new ldap.host = your_server_ip_address ldap.encryption(:method => :simple_tls) ldap.port = 639 - ldap.auth "admin", pass + ldap.auth "admin", pass # $ Alert ldap.bind end end @@ -56,4 +56,4 @@ class BarController < ApplicationController } ) end -end \ No newline at end of file +end diff --git a/ruby/ql/test/query-tests/experimental/InsecureRandomness/InsecureRandomness.qlref b/ruby/ql/test/query-tests/experimental/InsecureRandomness/InsecureRandomness.qlref index 8d04d215425..e3c5fbbad50 100644 --- a/ruby/ql/test/query-tests/experimental/InsecureRandomness/InsecureRandomness.qlref +++ b/ruby/ql/test/query-tests/experimental/InsecureRandomness/InsecureRandomness.qlref @@ -1 +1,2 @@ -experimental/insecure-randomness/InsecureRandomness.ql \ No newline at end of file +query: experimental/insecure-randomness/InsecureRandomness.ql +postprocess: utils/test/InlineExpectationsTestQuery.ql diff --git a/ruby/ql/test/query-tests/experimental/InsecureRandomness/InsecureRandomness.rb b/ruby/ql/test/query-tests/experimental/InsecureRandomness/InsecureRandomness.rb index 116957137b5..d56bebb30e7 100644 --- a/ruby/ql/test/query-tests/experimental/InsecureRandomness/InsecureRandomness.rb +++ b/ruby/ql/test/query-tests/experimental/InsecureRandomness/InsecureRandomness.rb @@ -3,7 +3,7 @@ require 'securerandom' def generate_password_1(length) chars = ('a'..'z').to_a + ('A'..'Z').to_a + ('0'..'9').to_a + ['!', '@', '#', '$', '%'] # BAD: rand is not cryptographically secure - password = (1..length).collect { chars[rand(chars.size)] }.join + password = (1..length).collect { chars[rand(chars.size)] }.join # $ Alert end def generate_password_2(length) @@ -16,4 +16,4 @@ def generate_password_2(length) end password = generate_password_1(10) -password = generate_password_2(10) \ No newline at end of file +password = generate_password_2(10) diff --git a/ruby/ql/test/query-tests/experimental/LdapInjection/LdapInjection.rb b/ruby/ql/test/query-tests/experimental/LdapInjection/LdapInjection.rb index 966b26ef636..6e258d9f180 100644 --- a/ruby/ql/test/query-tests/experimental/LdapInjection/LdapInjection.rb +++ b/ruby/ql/test/query-tests/experimental/LdapInjection/LdapInjection.rb @@ -2,11 +2,11 @@ class FooController < ActionController::Base def some_request_handler # A string tainted by user input is used directly as DN # (i.e a remote flow source) - dc = params[:dc] + dc = params[:dc] # $ Source # A string tainted by user input is used directly as search filter or attribute # (i.e a remote flow source) - name = params[:user_name] + name = params[:user_name] # $ Source # LDAP Connection ldap = Net::LDAP.new( @@ -22,20 +22,20 @@ class FooController < ActionController::Base # BAD: user input is used as DN # where dc is unsanitized - ldap.search(base: "ou=people,dc=#{dc},dc=com", filter: "cn=George", attributes: [""]) + ldap.search(base: "ou=people,dc=#{dc},dc=com", filter: "cn=George", attributes: [""]) # $ Alert # BAD: user input is used as search filter # where name is unsanitized - ldap.search(base: "ou=people,dc=example,dc=com", filter: "cn=#{name}", attributes: [""]) + ldap.search(base: "ou=people,dc=example,dc=com", filter: "cn=#{name}", attributes: [""]) # $ Alert # BAD: user input is used as attribute # where name is unsanitized - ldap.search(base: "ou=people,dc=example,dc=com", filter: "cn=George", attributes: [name]) + ldap.search(base: "ou=people,dc=example,dc=com", filter: "cn=George", attributes: [name]) # $ Alert # BAD: user input is used as search filter # where name is unsanitized filter = Net::LDAP::Filter.eq('cn', name) - ldap.search(base: "ou=people,dc=example,dc=com", filter: filter, attributes: [""]) + ldap.search(base: "ou=people,dc=example,dc=com", filter: filter, attributes: [""]) # $ Alert # GOOD: user input is not used in the LDAP query result = ldap.search(base: "ou=people,dc=example,dc=com", filter: "cn=George", attributes: [""]) @@ -63,4 +63,4 @@ class BarController < ApplicationController end result = ldap.search(base: "ou=people,dc=example,dc=com", filter: "cn=#{name}", attributes: [""]) end -end \ No newline at end of file +end diff --git a/ruby/ql/test/query-tests/experimental/LdapInjection/Ldapinjection.qlref b/ruby/ql/test/query-tests/experimental/LdapInjection/Ldapinjection.qlref index 7df75a91d96..f1164f044e6 100644 --- a/ruby/ql/test/query-tests/experimental/LdapInjection/Ldapinjection.qlref +++ b/ruby/ql/test/query-tests/experimental/LdapInjection/Ldapinjection.qlref @@ -1 +1,2 @@ -experimental/ldap-injection/LdapInjection.ql \ No newline at end of file +query: experimental/ldap-injection/LdapInjection.ql +postprocess: utils/test/InlineExpectationsTestQuery.ql diff --git a/ruby/ql/test/query-tests/experimental/TemplateInjection/ErbInjection.rb b/ruby/ql/test/query-tests/experimental/TemplateInjection/ErbInjection.rb index 41b9d706953..a433e4d5436 100644 --- a/ruby/ql/test/query-tests/experimental/TemplateInjection/ErbInjection.rb +++ b/ruby/ql/test/query-tests/experimental/TemplateInjection/ErbInjection.rb @@ -2,7 +2,7 @@ class FooController < ActionController::Base def some_request_handler # A string tainted by user input is inserted into a template # (i.e a remote flow source) - name = params[:name] + name = params[:name] # $ Source # Template with the source bad_text = " @@ -12,11 +12,11 @@ class FooController < ActionController::Base # BAD: user input is evaluated # where name is unsanitized - template = ERB.new(bad_text).result(binding) + template = ERB.new(bad_text).result(binding) # $ Alert # BAD: user input is evaluated # where name is unsanitized - render inline: bad_text + render inline: bad_text # $ Alert # Template with the source good_text = " diff --git a/ruby/ql/test/query-tests/experimental/TemplateInjection/SlimInjection.rb b/ruby/ql/test/query-tests/experimental/TemplateInjection/SlimInjection.rb index 07b93a20468..0b7fbc478db 100644 --- a/ruby/ql/test/query-tests/experimental/TemplateInjection/SlimInjection.rb +++ b/ruby/ql/test/query-tests/experimental/TemplateInjection/SlimInjection.rb @@ -2,7 +2,7 @@ class FooController < ActionController::Base def some_request_handler # A string tainted by user input is inserted into a template # (i.e a remote flow source) - name = params[:name] + name = params[:name] # $ Source # Template with the source (no sanitizer) bad_text = " @@ -11,7 +11,7 @@ class FooController < ActionController::Base " % name # BAD: renders user input # where text is unsanitized - Slim::Template.new{ bad_text }.render + Slim::Template.new{ bad_text }.render # $ Alert # Template with the source (no sanitizer) bad2_text = " @@ -20,7 +20,7 @@ class FooController < ActionController::Base " # BAD: renders user input # where text is unsanitized - Slim::Template.new{ bad2_text }.render + Slim::Template.new{ bad2_text }.render # $ Alert # Template with the source (no render) good_text = " @@ -64,4 +64,4 @@ class BarController < ApplicationController " % name2 template_bar1 = Slim::Template.new{ text_bar2 }.render end -end \ No newline at end of file +end diff --git a/ruby/ql/test/query-tests/experimental/TemplateInjection/TemplateInjection.qlref b/ruby/ql/test/query-tests/experimental/TemplateInjection/TemplateInjection.qlref index 38054e393ee..e783cc8cabb 100644 --- a/ruby/ql/test/query-tests/experimental/TemplateInjection/TemplateInjection.qlref +++ b/ruby/ql/test/query-tests/experimental/TemplateInjection/TemplateInjection.qlref @@ -1 +1,2 @@ -experimental/template-injection/TemplateInjection.ql \ No newline at end of file +query: experimental/template-injection/TemplateInjection.ql +postprocess: utils/test/InlineExpectationsTestQuery.ql diff --git a/ruby/ql/test/query-tests/experimental/XPathInjection/LibxmlInjection.rb b/ruby/ql/test/query-tests/experimental/XPathInjection/LibxmlInjection.rb index 3bde2f1e40b..8a992b5ba36 100644 --- a/ruby/ql/test/query-tests/experimental/XPathInjection/LibxmlInjection.rb +++ b/ruby/ql/test/query-tests/experimental/XPathInjection/LibxmlInjection.rb @@ -2,7 +2,7 @@ require 'libxml' class FooController < ActionController::Base def libxml_handler(event:, context:) - name = params[:user_name] + name = params[:user_name] # $ Source xml = <<-XML @@ -18,13 +18,13 @@ class FooController < ActionController::Base results1 = doc.find_first('//foo') # BAD: XPath query is constructed from user input - results2 = doc.find_first("//#{name}") + results2 = doc.find_first("//#{name}") # $ Alert # GOOD: XPath query is not constructed from user input results3 = doc.find('//foo') # BAD: XPath query is constructed from user input - results4 = doc.find("//#{name}") + results4 = doc.find("//#{name}") # $ Alert end end diff --git a/ruby/ql/test/query-tests/experimental/XPathInjection/NokogiriInjection.rb b/ruby/ql/test/query-tests/experimental/XPathInjection/NokogiriInjection.rb index e3ac8055f48..e782d923034 100644 --- a/ruby/ql/test/query-tests/experimental/XPathInjection/NokogiriInjection.rb +++ b/ruby/ql/test/query-tests/experimental/XPathInjection/NokogiriInjection.rb @@ -2,7 +2,7 @@ require 'nokogiri' class FooController < ActionController::Base def nokogiri_handler(event:, context:) - name = params[:user_name] + name = params[:user_name] # $ Source xml = <<-XML @@ -18,19 +18,19 @@ class FooController < ActionController::Base results1 = doc.at('//foo') # BAD: XPath query is constructed from user input - results2 = doc.at("//#{name}") + results2 = doc.at("//#{name}") # $ Alert # GOOD: XPath query is not constructed from user input results3 = doc.xpath('//foo') # BAD: XPath query is constructed from user input - results4 = doc.xpath("//#{name}") + results4 = doc.xpath("//#{name}") # $ Alert # GOOD: XPath query is not constructed from user input results5 = doc.at_xpath('//foo') # BAD: XPath query is constructed from user input - results6 = doc.at_xpath("//#{name}") + results6 = doc.at_xpath("//#{name}") # $ Alert # GOOD: XPath query is not constructed from user input doc.xpath('//foo').each do |element| @@ -38,7 +38,7 @@ class FooController < ActionController::Base end # BAD: XPath query constructed from user input - doc.xpath("//#{name}").each do |element| + doc.xpath("//#{name}").each do |element| # $ Alert puts element.text end @@ -48,7 +48,7 @@ class FooController < ActionController::Base end # BAD: XPath query constructed from user input - doc.search("//#{name}").each do |element| + doc.search("//#{name}").each do |element| # $ Alert puts element.text end end @@ -85,4 +85,4 @@ class BarController < ActionController::Base results9 = doc.at_xpath("//#{safe_name}") end -end \ No newline at end of file +end diff --git a/ruby/ql/test/query-tests/experimental/XPathInjection/RexmlInjection.rb b/ruby/ql/test/query-tests/experimental/XPathInjection/RexmlInjection.rb index 6ee16d125b4..87ceb2cbb3c 100644 --- a/ruby/ql/test/query-tests/experimental/XPathInjection/RexmlInjection.rb +++ b/ruby/ql/test/query-tests/experimental/XPathInjection/RexmlInjection.rb @@ -2,7 +2,7 @@ require 'rexml' class FooController < ActionController::Base def rexml_handler(event:, context:) - name = params[:user_name] + name = params[:user_name] # $ Source xml = <<-XML @@ -18,13 +18,13 @@ class FooController < ActionController::Base results1 = REXML::XPath.first(doc, "//foo") # BAD: XPath query is constructed from user input - results2 = REXML::XPath.first(doc, "//#{name}") + results2 = REXML::XPath.first(doc, "//#{name}") # $ Alert # GOOD: XPath query is not constructed from user input results3 = REXML::XPath.match(doc, "//foo", nil) # BAD: XPath query is constructed from user input - results4 = REXML::XPath.match(doc, "//#{name}", nil) + results4 = REXML::XPath.match(doc, "//#{name}", nil) # $ Alert # GOOD: XPath query is not constructed from user input REXML::XPath.each(doc, "//foo") do |element| @@ -32,7 +32,7 @@ class FooController < ActionController::Base end # BAD: XPath query constructed from user input - REXML::XPath.each(doc, "//#{name}") do |element| + REXML::XPath.each(doc, "//#{name}") do |element| # $ Alert puts element.text end end @@ -66,4 +66,4 @@ class BarController < ActionController::Base results6 = REXML::XPath.match(doc, "//#{safe_name}", nil) end -end \ No newline at end of file +end diff --git a/ruby/ql/test/query-tests/experimental/XPathInjection/XPathInjection.qlref b/ruby/ql/test/query-tests/experimental/XPathInjection/XPathInjection.qlref index a5b1b23c203..7ca9780f11c 100644 --- a/ruby/ql/test/query-tests/experimental/XPathInjection/XPathInjection.qlref +++ b/ruby/ql/test/query-tests/experimental/XPathInjection/XPathInjection.qlref @@ -1 +1,2 @@ -experimental/xpath-injection/XpathInjection.ql \ No newline at end of file +query: experimental/xpath-injection/XpathInjection.ql +postprocess: utils/test/InlineExpectationsTestQuery.ql diff --git a/ruby/ql/test/query-tests/experimental/cwe-022-ZipSlip/ZipSlip.qlref b/ruby/ql/test/query-tests/experimental/cwe-022-ZipSlip/ZipSlip.qlref index 2ecd57e4b2b..a5b8c00322e 100644 --- a/ruby/ql/test/query-tests/experimental/cwe-022-ZipSlip/ZipSlip.qlref +++ b/ruby/ql/test/query-tests/experimental/cwe-022-ZipSlip/ZipSlip.qlref @@ -1 +1,2 @@ -experimental/cwe-022-zipslip/ZipSlip.ql +query: experimental/cwe-022-zipslip/ZipSlip.ql +postprocess: utils/test/InlineExpectationsTestQuery.ql diff --git a/ruby/ql/test/query-tests/experimental/cwe-022-ZipSlip/zip_slip.rb b/ruby/ql/test/query-tests/experimental/cwe-022-ZipSlip/zip_slip.rb index 4e5aa27d00a..72c8c4701fc 100644 --- a/ruby/ql/test/query-tests/experimental/cwe-022-ZipSlip/zip_slip.rb +++ b/ruby/ql/test/query-tests/experimental/cwe-022-ZipSlip/zip_slip.rb @@ -5,9 +5,9 @@ class TestController < ActionController::Base def tarReaderUnsafe path = params[:path] file_stream = IO.new(IO.sysopen(path)) - tarfile = Gem::Package::TarReader.new(file_stream) + tarfile = Gem::Package::TarReader.new(file_stream) # $ Source tarfile.each do |entry| - ::File.open(entry.full_name, "wb") do |os| + ::File.open(entry.full_name, "wb") do |os| # $ Alert entry.read end end @@ -17,9 +17,9 @@ class TestController < ActionController::Base def tarReaderBlockUnsafe path = params[:path] file_stream = IO.new(IO.sysopen(path)) - Gem::Package::TarReader.new(file_stream) do |tarfile| + Gem::Package::TarReader.new(file_stream) do |tarfile| # $ Source tarfile.each_entry do |entry| - ::File.open(entry.full_name, "wb") do |os| + ::File.open(entry.full_name, "wb") do |os| # $ Alert entry.read end end @@ -43,8 +43,8 @@ class TestController < ActionController::Base # BAD def zipFileUnsafe path = params[:path] - Zip::File.open(path).each do |entry| - File.open(entry.name, "wb") do |os| + Zip::File.open(path).each do |entry| # $ Source + File.open(entry.name, "wb") do |os| # $ Alert entry.read end end @@ -53,9 +53,9 @@ class TestController < ActionController::Base # BAD def zipFileBlockUnsafe path = params[:path] - Zip::File.open(path) do |zip_file| + Zip::File.open(path) do |zip_file| # $ Source zip_file.each do |entry| - File.open(entry.name, "wb") do |os| + File.open(entry.name, "wb") do |os| # $ Alert entry.read end end @@ -87,7 +87,7 @@ class TestController < ActionController::Base end def get_compressed_file_stream(compressed_file_path) - gzip = Zlib::GzipReader.open(compressed_file_path) + gzip = Zlib::GzipReader.open(compressed_file_path) # $ Source yield(gzip) end @@ -97,7 +97,7 @@ class TestController < ActionController::Base get_compressed_file_stream(path) do |compressed_file| compressed_file.each do |entry| entry_path = entry.full_name - ::File.open(entry_path, 'wb') do |os| + ::File.open(entry_path, 'wb') do |os| # $ Alert entry.read end end @@ -120,10 +120,10 @@ class TestController < ActionController::Base def gzipReaderUnsafeNewInstance path = params[:path] File.open(path, 'rb') do |f| - gz = Zlib::GzipReader.new(f) + gz = Zlib::GzipReader.new(f) # $ Source gz.each do |entry| entry_path = entry.full_name - ::File.open(entry_path, 'wb') do |os| + ::File.open(entry_path, 'wb') do |os| # $ Alert entry.read end end diff --git a/ruby/ql/test/query-tests/experimental/cwe-176/UnicodeBypassValidation.qlref b/ruby/ql/test/query-tests/experimental/cwe-176/UnicodeBypassValidation.qlref index 2faba2ebb12..a13083c07d5 100644 --- a/ruby/ql/test/query-tests/experimental/cwe-176/UnicodeBypassValidation.qlref +++ b/ruby/ql/test/query-tests/experimental/cwe-176/UnicodeBypassValidation.qlref @@ -1 +1,2 @@ -experimental/cwe-176/UnicodeBypassValidation.ql +query: experimental/cwe-176/UnicodeBypassValidation.ql +postprocess: utils/test/InlineExpectationsTestQuery.ql diff --git a/ruby/ql/test/query-tests/experimental/cwe-176/unicode_normalization.rb b/ruby/ql/test/query-tests/experimental/cwe-176/unicode_normalization.rb index a7b77cc3a66..e158bc47fdd 100644 --- a/ruby/ql/test/query-tests/experimental/cwe-176/unicode_normalization.rb +++ b/ruby/ql/test/query-tests/experimental/cwe-176/unicode_normalization.rb @@ -4,35 +4,35 @@ require 'cgi' class UnicodeNormalizationOKController < ActionController::Base def unicodeNormalize - unicode_input = params[:unicode_input] - normalized_nfkc = unicode_input.unicode_normalize(:nfkc) # $ MISSING:result=OK - normalized_nfc = unicode_input.unicode_normalize(:nfc) # $ MISSING:result=OK + unicode_input = params[:unicode_input] # $ Source + normalized_nfkc = unicode_input.unicode_normalize(:nfkc) # $ Alert // $ MISSING:result=OK + normalized_nfc = unicode_input.unicode_normalize(:nfc) # $ Alert // $ MISSING:result=OK end end class UnicodeNormalizationStrManipulationController < ActionController::Base def unicodeNormalize - unicode_input = params[:unicode_input] - unicode_input_manip = unicode_input.sub(/[aeiou]/, "*") - normalized_nfkc = unicode_input_manip.unicode_normalize(:nfkc) # $ result=BAD - normalized_nfc = unicode_input_manip.unicode_normalize(:nfc) # $ result=BAD + unicode_input = params[:unicode_input] # $ Source + unicode_input_manip = unicode_input.sub(/[aeiou]/, "*") # $ Source + normalized_nfkc = unicode_input_manip.unicode_normalize(:nfkc) # $ Alert // $ result=BAD + normalized_nfc = unicode_input_manip.unicode_normalize(:nfc) # $ Alert // $ result=BAD end end class UnicodeNormalizationHtMLEscapeController < ActionController::Base def unicodeNormalize - unicode_input = params[:unicode_input] - unicode_html_safe = html_escape(unicode_input) - normalized_nfkc = unicode_html_safe.unicode_normalize(:nfkc) # $ result=BAD - normalized_nfc = unicode_html_safe.unicode_normalize(:nfc) # $ result=BAD + unicode_input = params[:unicode_input] # $ Source + unicode_html_safe = html_escape(unicode_input) # $ Source + normalized_nfkc = unicode_html_safe.unicode_normalize(:nfkc) # $ Alert // $ result=BAD + normalized_nfc = unicode_html_safe.unicode_normalize(:nfc) # $ Alert // $ result=BAD end end class UnicodeNormalizationCGIHtMLEscapeController < ActionController::Base def unicodeNormalize - unicode_input = params[:unicode_input] - unicode_html_safe = CGI.escapeHTML(unicode_input).html_safe - normalized_nfkc = unicode_html_safe.unicode_normalize(:nfkd) # $ result=BAD - normalized_nfc = unicode_html_safe.unicode_normalize(:nfd) # $ result=BAD + unicode_input = params[:unicode_input] # $ Source + unicode_html_safe = CGI.escapeHTML(unicode_input).html_safe # $ Source + normalized_nfkc = unicode_html_safe.unicode_normalize(:nfkd) # $ Alert // $ result=BAD + normalized_nfc = unicode_html_safe.unicode_normalize(:nfd) # $ Alert // $ result=BAD end end diff --git a/ruby/ql/test/query-tests/experimental/cwe-347/EmptyJWTSecret.qlref b/ruby/ql/test/query-tests/experimental/cwe-347/EmptyJWTSecret.qlref index 3d034add0ba..c6f2acf7d75 100644 --- a/ruby/ql/test/query-tests/experimental/cwe-347/EmptyJWTSecret.qlref +++ b/ruby/ql/test/query-tests/experimental/cwe-347/EmptyJWTSecret.qlref @@ -1 +1,2 @@ -experimental/cwe-347/EmptyJWTSecret.ql \ No newline at end of file +query: experimental/cwe-347/EmptyJWTSecret.ql +postprocess: utils/test/InlineExpectationsTestQuery.ql diff --git a/ruby/ql/test/query-tests/experimental/cwe-347/EmptyJWTSecret.rb b/ruby/ql/test/query-tests/experimental/cwe-347/EmptyJWTSecret.rb index a78ec0d0421..68cdb179c75 100644 --- a/ruby/ql/test/query-tests/experimental/cwe-347/EmptyJWTSecret.rb +++ b/ruby/ql/test/query-tests/experimental/cwe-347/EmptyJWTSecret.rb @@ -6,10 +6,10 @@ payload = { foo: 'bar' } token1 = JWT.encode({ foo: 'bar' }, "secret", 'none') # BAD: the secret used is empty -token2 = JWT.encode({ foo: 'bar' }, nil, 'HS256') +token2 = JWT.encode({ foo: 'bar' }, nil, 'HS256') # $ Alert[rb/jwt-empty-secret-or-algorithm] # BAD: the secret used is empty -token3 = JWT.encode({ foo: 'bar' }, "", 'HS256') +token3 = JWT.encode({ foo: 'bar' }, "", 'HS256') # $ Alert[rb/jwt-empty-secret-or-algorithm] # GOOD: the token is signed -token4 = JWT.encode({ foo: 'bar' }, "secret", 'HS256') \ No newline at end of file +token4 = JWT.encode({ foo: 'bar' }, "secret", 'HS256') diff --git a/ruby/ql/test/query-tests/experimental/cwe-347/MissingJWTVerification.qlref b/ruby/ql/test/query-tests/experimental/cwe-347/MissingJWTVerification.qlref index 793275aef11..dba60e5fbb4 100644 --- a/ruby/ql/test/query-tests/experimental/cwe-347/MissingJWTVerification.qlref +++ b/ruby/ql/test/query-tests/experimental/cwe-347/MissingJWTVerification.qlref @@ -1 +1,2 @@ -experimental/cwe-347/MissingJWTVerification.ql \ No newline at end of file +query: experimental/cwe-347/MissingJWTVerification.ql +postprocess: utils/test/InlineExpectationsTestQuery.ql diff --git a/ruby/ql/test/query-tests/experimental/cwe-347/MissingJWTVerification.rb b/ruby/ql/test/query-tests/experimental/cwe-347/MissingJWTVerification.rb index 4c5bd08094e..cf7fc7cbf8e 100644 --- a/ruby/ql/test/query-tests/experimental/cwe-347/MissingJWTVerification.rb +++ b/ruby/ql/test/query-tests/experimental/cwe-347/MissingJWTVerification.rb @@ -3,22 +3,22 @@ require 'jwt' payload = { foo: 'bar' } # Unsecure token -token_without_signature = JWT.encode(payload, nil, 'none') +token_without_signature = JWT.encode(payload, nil, 'none') # $ Alert[rb/jwt-empty-secret-or-algorithm] # Secure token token = JWT.encode(payload, "secret", 'HS256') # BAD: it does not verify -decoded_token1 = JWT.decode(token_without_signature, nil, false, algorithm: 'HS256') +decoded_token1 = JWT.decode(token_without_signature, nil, false, algorithm: 'HS256') # $ Alert[rb/jwt-missing-verification] # BAD: it's using none -decoded_token3 = JWT.decode(token_without_signature, secret, true, algorithm: 'none') +decoded_token3 = JWT.decode(token_without_signature, secret, true, algorithm: 'none') # $ Alert[rb/jwt-missing-verification] # BAD: it's using none -decoded_token4 = JWT.decode(token_without_signature, secret, true, { algorithm: 'none' }) +decoded_token4 = JWT.decode(token_without_signature, secret, true, { algorithm: 'none' }) # $ Alert[rb/jwt-missing-verification] # GOOD: it does verify decoded_token5 = JWT.decode(token, secret, 'HS256') # GOOD: it does verify -decoded_token2 = JWT.decode(token,secret) \ No newline at end of file +decoded_token2 = JWT.decode(token,secret) diff --git a/ruby/ql/test/query-tests/experimental/cwe-502/UnsafeYamlDeserialization.qlref b/ruby/ql/test/query-tests/experimental/cwe-502/UnsafeYamlDeserialization.qlref index 991ba757e43..f7fb7dfe3fc 100644 --- a/ruby/ql/test/query-tests/experimental/cwe-502/UnsafeYamlDeserialization.qlref +++ b/ruby/ql/test/query-tests/experimental/cwe-502/UnsafeYamlDeserialization.qlref @@ -1 +1,2 @@ -experimental/cwe-502/UnsafeYamlDeserialization.ql \ No newline at end of file +query: experimental/cwe-502/UnsafeYamlDeserialization.ql +postprocess: utils/test/InlineExpectationsTestQuery.ql diff --git a/ruby/ql/test/query-tests/experimental/cwe-502/UnsafeYamlDeserialization.rb b/ruby/ql/test/query-tests/experimental/cwe-502/UnsafeYamlDeserialization.rb index c9b186e0915..dc3e1cbab95 100644 --- a/ruby/ql/test/query-tests/experimental/cwe-502/UnsafeYamlDeserialization.rb +++ b/ruby/ql/test/query-tests/experimental/cwe-502/UnsafeYamlDeserialization.rb @@ -7,15 +7,15 @@ require "yaml" class UsersController < ActionController::Base # BAD before psych version 4.0.0 and def route1 - yaml_data = params[:key] - object = Psych.load yaml_data + yaml_data = params[:key] # $ Source + object = Psych.load yaml_data # $ Alert object = Psych.load_file yaml_data end # GOOD In psych version 4.0.0 and above def route2 - yaml_data = params[:key] - object = Psych.load yaml_data + yaml_data = params[:key] # $ Source + object = Psych.load yaml_data # $ Alert object = Psych.load_file yaml_data end @@ -29,14 +29,14 @@ class UsersController < ActionController::Base # BAD def route4 - yaml_data = params[:key] - object = Psych.unsafe_load(yaml_data) - object = Psych.unsafe_load_file(yaml_data) - object = Psych.load_stream(yaml_data) + yaml_data = params[:key] # $ Source + object = Psych.unsafe_load(yaml_data) # $ Alert + object = Psych.unsafe_load_file(yaml_data) # $ Alert + object = Psych.load_stream(yaml_data) # $ Alert parse_output = Psych.parse_stream(yaml_data) - object = parse_output.to_ruby - object = Psych.parse(yaml_data).to_ruby - object = Psych.parse_file(yaml_data).to_ruby + object = parse_output.to_ruby # $ Alert + object = Psych.parse(yaml_data).to_ruby # $ Alert + object = Psych.parse_file(yaml_data).to_ruby # $ Alert parsed_yaml = Psych.parse_stream(yaml_data) parsed_yaml.children.each do |child| object = child.to_ruby @@ -46,7 +46,7 @@ class UsersController < ActionController::Base end object = parsed_yaml.children.first.to_ruby content = parsed_yaml.children[0].children[0].children - object = parsed_yaml.to_ruby[0] + object = parsed_yaml.to_ruby[0] # $ Alert object = content.to_ruby[0] object = Psych.parse(yaml_data).children[0].to_ruby end @@ -58,18 +58,18 @@ class UsersController < ActionController::Base end def stdin - object = YAML.load $stdin.read + object = YAML.load $stdin.read # $ Alert # STDIN - object = YAML.load STDIN.gets + object = YAML.load STDIN.gets # $ Alert # ARGF - object = YAML.load ARGF.read + object = YAML.load ARGF.read # $ Alert # Kernel.gets - object = YAML.load gets + object = YAML.load gets # $ Alert # Kernel.readlines - object = YAML.load readlines + object = YAML.load readlines # $ Alert end end diff --git a/ruby/ql/test/query-tests/experimental/manually-check-http-verb/ManuallyCheckHttpVerb.qlref b/ruby/ql/test/query-tests/experimental/manually-check-http-verb/ManuallyCheckHttpVerb.qlref index 463c21cd0f2..455d02aef04 100644 --- a/ruby/ql/test/query-tests/experimental/manually-check-http-verb/ManuallyCheckHttpVerb.qlref +++ b/ruby/ql/test/query-tests/experimental/manually-check-http-verb/ManuallyCheckHttpVerb.qlref @@ -1 +1,2 @@ -experimental/manually-check-http-verb/ManuallyCheckHttpVerb.ql \ No newline at end of file +query: experimental/manually-check-http-verb/ManuallyCheckHttpVerb.ql +postprocess: utils/test/InlineExpectationsTestQuery.ql diff --git a/ruby/ql/test/query-tests/experimental/manually-check-http-verb/ManuallyCheckHttpVerb.rb b/ruby/ql/test/query-tests/experimental/manually-check-http-verb/ManuallyCheckHttpVerb.rb index 055e9d98638..aacb1730dd9 100644 --- a/ruby/ql/test/query-tests/experimental/manually-check-http-verb/ManuallyCheckHttpVerb.rb +++ b/ruby/ql/test/query-tests/experimental/manually-check-http-verb/ManuallyCheckHttpVerb.rb @@ -1,39 +1,39 @@ class ExampleController < ActionController::Base # Should find def example_action - if request.get? + if request.get? # $ Alert Resource.find(id: params[:example_id]) end end # Should find def other_action - method = request.env['REQUEST_METHOD'] - if method == "GET" + method = request.env['REQUEST_METHOD'] # $ Source + if method == "GET" # $ Alert Resource.find(id: params[:id]) end end # Should find def foo - method = request.request_method - if method == "GET" + method = request.request_method # $ Source + if method == "GET" # $ Alert Resource.find(id: params[:id]) end end # Should find def bar - method = request.method - if method == "GET" + method = request.method # $ Source + if method == "GET" # $ Alert Resource.find(id: params[:id]) end end # Should find def baz - method = request.raw_request_method - if method == "GET" + method = request.raw_request_method # $ Source + if method == "GET" # $ Alert Resource.find(id: params[:id]) end end @@ -48,15 +48,15 @@ class ExampleController < ActionController::Base # Should find def foobarbaz - method = request.request_method_symbol - if method == :GET + method = request.request_method_symbol # $ Source + if method == :GET # $ Alert Resource.find(id: params[:id]) end end # Should find def resource_action - case request.env['REQUEST_METHOD'] + case request.env['REQUEST_METHOD'] # $ Alert when "GET" Resource.find(id: params[:id]) when "POST" @@ -114,4 +114,4 @@ class NotAController end class Resource < ActiveRecord::Base -end \ No newline at end of file +end diff --git a/ruby/ql/test/query-tests/experimental/weak-params/WeakParams.qlref b/ruby/ql/test/query-tests/experimental/weak-params/WeakParams.qlref index 5350e4bf40a..96a41103dd4 100644 --- a/ruby/ql/test/query-tests/experimental/weak-params/WeakParams.qlref +++ b/ruby/ql/test/query-tests/experimental/weak-params/WeakParams.qlref @@ -1 +1,2 @@ -experimental/weak-params/WeakParams.ql \ No newline at end of file +query: experimental/weak-params/WeakParams.ql +postprocess: utils/test/InlineExpectationsTestQuery.ql diff --git a/ruby/ql/test/query-tests/experimental/weak-params/WeakParams.rb b/ruby/ql/test/query-tests/experimental/weak-params/WeakParams.rb index a5edef2e6dc..461bd4a5328 100644 --- a/ruby/ql/test/query-tests/experimental/weak-params/WeakParams.rb +++ b/ruby/ql/test/query-tests/experimental/weak-params/WeakParams.rb @@ -2,22 +2,22 @@ class TestController < ActionController::Base # Should catch def create - TestObject.create(foo: request.request_parameters[:foo]) + TestObject.create(foo: request.request_parameters[:foo]) # $ Alert end # Should catch def create_query - TestObject.create(foo: request.query_parameters[:foo]) + TestObject.create(foo: request.query_parameters[:foo]) # $ Alert end # Should catch def update_unsafe - TestObject.update(foo: request.POST[:foo]) + TestObject.update(foo: request.POST[:foo]) # $ Alert end # Should catch def update_unsafe_get - TestObject.update(foo: request.GET[:foo]) + TestObject.update(foo: request.GET[:foo]) # $ Alert end # Should not catch @@ -37,4 +37,4 @@ class TestController < ActionController::Base end class TestObject < ActiveRecord::Base -end \ No newline at end of file +end diff --git a/ruby/ql/test/query-tests/performance/UseDetect/UseDetect.qlref b/ruby/ql/test/query-tests/performance/UseDetect/UseDetect.qlref index f2a94b28c40..453e0a3f399 100644 --- a/ruby/ql/test/query-tests/performance/UseDetect/UseDetect.qlref +++ b/ruby/ql/test/query-tests/performance/UseDetect/UseDetect.qlref @@ -1 +1,2 @@ -experimental/performance/UseDetect.ql +query: experimental/performance/UseDetect.ql +postprocess: utils/test/InlineExpectationsTestQuery.ql diff --git a/ruby/ql/test/query-tests/performance/UseDetect/UseDetect.rb b/ruby/ql/test/query-tests/performance/UseDetect/UseDetect.rb index e1d2d9b91ba..2c2602e72e6 100644 --- a/ruby/ql/test/query-tests/performance/UseDetect/UseDetect.rb +++ b/ruby/ql/test/query-tests/performance/UseDetect/UseDetect.rb @@ -2,14 +2,14 @@ class DetectTest def test # These are bad - [].select { |i| true }.first - [].select { |i| true }.last - [].select { |i| true }[0] - [].select { |i| true }[-1] - [].filter { |i| true }.first - [].find_all { |i| true }.last + [].select { |i| true }.first # $ Alert + [].select { |i| true }.last # $ Alert + [].select { |i| true }[0] # $ Alert + [].select { |i| true }[-1] # $ Alert + [].filter { |i| true }.first # $ Alert + [].find_all { |i| true }.last # $ Alert selection1 = [].select { |i| true } - selection1.first + selection1.first # $ Alert # These are good [].select("").first # Selecting a string diff --git a/ruby/ql/test/query-tests/security/cwe-020/IncompleteHostnameRegExp/IncompleteHostnameRegExp.qlref b/ruby/ql/test/query-tests/security/cwe-020/IncompleteHostnameRegExp/IncompleteHostnameRegExp.qlref index 7fd45d159ce..93a6200ff17 100644 --- a/ruby/ql/test/query-tests/security/cwe-020/IncompleteHostnameRegExp/IncompleteHostnameRegExp.qlref +++ b/ruby/ql/test/query-tests/security/cwe-020/IncompleteHostnameRegExp/IncompleteHostnameRegExp.qlref @@ -1 +1,2 @@ -queries/security/cwe-020/IncompleteHostnameRegExp.ql \ No newline at end of file +query: queries/security/cwe-020/IncompleteHostnameRegExp.ql +postprocess: utils/test/InlineExpectationsTestQuery.ql diff --git a/ruby/ql/test/query-tests/security/cwe-020/IncompleteHostnameRegExp/hosttest.rb b/ruby/ql/test/query-tests/security/cwe-020/IncompleteHostnameRegExp/hosttest.rb index 5a5c96692ce..32aa8ad9491 100644 --- a/ruby/ql/test/query-tests/security/cwe-020/IncompleteHostnameRegExp/hosttest.rb +++ b/ruby/ql/test/query-tests/security/cwe-020/IncompleteHostnameRegExp/hosttest.rb @@ -1,6 +1,6 @@ -UNSAFE_REGEX1 = /(www|beta).example.com\// -UNSAFE_REGEX2 = Regexp.compile("(www|beta).example.com/") -UNSAFE_REGEX3 = Regexp.new("(www|beta).example.com/") +UNSAFE_REGEX1 = /(www|beta).example.com\// # $ Alert +UNSAFE_REGEX2 = Regexp.compile("(www|beta).example.com/") # $ Alert +UNSAFE_REGEX3 = Regexp.new("(www|beta).example.com/") # $ Alert SAFE_REGEX = /(www|beta)\.example\.com\// def unsafe diff --git a/ruby/ql/test/query-tests/security/cwe-020/IncompleteHostnameRegExp/tst-IncompleteHostnameRegExp.rb b/ruby/ql/test/query-tests/security/cwe-020/IncompleteHostnameRegExp/tst-IncompleteHostnameRegExp.rb index 7041e4dc9c4..50e2e257dce 100644 --- a/ruby/ql/test/query-tests/security/cwe-020/IncompleteHostnameRegExp/tst-IncompleteHostnameRegExp.rb +++ b/ruby/ql/test/query-tests/security/cwe-020/IncompleteHostnameRegExp/tst-IncompleteHostnameRegExp.rb @@ -1,62 +1,62 @@ def foo /^http:\/\/example.com/; # OK - /^http:\/\/test.example.com/; # NOT OK + /^http:\/\/test.example.com/; # $ Alert // NOT OK /^http:\/\/test\.example.com/; # OK - /^http:\/\/test.example.net/; # NOT OK - /^http:\/\/test.(example-a|example-b).com/; # NOT OK - /^http:\/\/(.+).example.com\//; # NOT OK + /^http:\/\/test.example.net/; # $ Alert // NOT OK + /^http:\/\/test.(example-a|example-b).com/; # $ Alert // NOT OK + /^http:\/\/(.+).example.com\//; # $ Alert // NOT OK /^http:\/\/(\.+)\.example.com/; # OK - /^http:\/\/(?:.+)\.test\.example.com\//; # NOT OK - /^http:\/\/test.example.com\/(?:.*)/; # OK - Regexp.new("^http://test.example.com"); # NOT OK - if (s.match("^http://test.example.com")); end # NOT OK + /^http:\/\/(?:.+)\.test\.example.com\//; # $ Alert // NOT OK + /^http:\/\/test.example.com\/(?:.*)/; # $ Alert // OK + Regexp.new("^http://test.example.com"); # $ Alert // NOT OK + if (s.match("^http://test.example.com")); end # $ Alert // NOT OK - Regexp.new(id(id(id("^http://test.example.com")))); # NOT OK + Regexp.new(id(id(id("^http://test.example.com")))); # $ Alert // NOT OK - Regexp.new(`test.example.com$`); # NOT OK + Regexp.new(`test.example.com$`); # $ Alert // NOT OK - hostname = '^test.example.com'; # NOT OK - Regexp.new("#{hostname}$"); + hostname = '^test.example.com'; # $ Alert // NOT OK + Regexp.new("#{hostname}$"); # $ Alert - domain = { hostname: 'test.example.com$' }; # NOT OK + domain = { hostname: 'test.example.com$' }; # $ Alert // NOT OK Regexp.new(domain[:hostname]); - convert1({ hostname: 'test.example.com$' }); # NOT OK + convert1({ hostname: 'test.example.com$' }); # $ Alert // NOT OK - domains = [ { hostname: 'test.example.com$' } ]; # NOT OK - but not flagged due to limitations of TypeTracking. + domains = [ { hostname: 'test.example.com$' } ]; # $ MISSING: Alert # NOT OK - but not flagged due to limitations of TypeTracking. domains.map{ |d| convert2(d) }; /^(.+\.(?:example-a|example-b)\.com)\//; # NOT OK - /^(https?:)?\/\/((service|www).)?example.com(?=$|\/)/; # NOT OK - /^(http|https):\/\/www.example.com\/p\/f\//; # NOT OK - /^(http:\/\/sub.example.com\/)/i; # NOT OK - /^https?:\/\/api.example.com/; # NOT OK - Regexp.new('^http://localhost:8000|' + "^https?://.+\\.example\\.com/"); # NOT OK - Regexp.new("^http[s]?:\/\/?sub1\\.sub2\\.example\\.com\/f\/(.+)"); # NOT OK - /^https:\/\/[a-z]*.example.com$/; # NOT OK - Regexp.compile('^protos?://(localhost|.+.example.net|.+.example-a.com|.+.example-b.com|.+.example.internal)'); # NOT OK + /^(https?:)?\/\/((service|www).)?example.com(?=$|\/)/; # $ Alert // NOT OK + /^(http|https):\/\/www.example.com\/p\/f\//; # $ Alert // NOT OK + /^(http:\/\/sub.example.com\/)/i; # $ Alert // NOT OK + /^https?:\/\/api.example.com/; # $ Alert // NOT OK + Regexp.new('^http://localhost:8000|' + "^https?://.+\\.example\\.com/"); # $ Alert // NOT OK + Regexp.new("^http[s]?:\/\/?sub1\\.sub2\\.example\\.com\/f\/(.+)"); # $ MISSING: Alert # NOT OK + /^https:\/\/[a-z]*.example.com$/; # $ Alert // NOT OK + Regexp.compile('^protos?://(localhost|.+.example.net|.+.example-a.com|.+.example-b.com|.+.example.internal)'); # $ Alert // NOT OK /^(example.dev|example.com)/; # OK - Regexp.new('^http://localhost:8000|' + "^https?://.+.example\\.com/"); # NOT OK + Regexp.new('^http://localhost:8000|' + "^https?://.+.example\\.com/"); # $ Alert // NOT OK primary = 'example.com$'; - Regexp.new('test.' + primary); # NOT OK, but not detected + Regexp.new('test.' + primary); # $ MISSING: Alert # NOT OK, but not detected - Regexp.new('test.' + 'example.com$'); # NOT OK + Regexp.new('test.' + 'example.com$'); # $ MISSING: Alert # NOT OK - Regexp.new('^http://test\.example.com'); # NOT OK + Regexp.new('^http://test\.example.com'); # $ MISSING: Alert # NOT OK /^http:\/\/(..|...)\.example\.com\/index\.html/; # OK, wildcards are intentional /^http:\/\/.\.example\.com\/index\.html/; # OK, the wildcard is intentional - /^(foo.example\.com|whatever)$/; # kinda OK - one disjunction doesn't even look like a hostname + /^(foo.example\.com|whatever)$/; # $ Alert // kinda OK - one disjunction doesn't even look like a hostname end def id(e); return e; end def convert1(domain) @@ -78,4 +78,4 @@ class B end end -B.match?("^http://test.example.com") # NOT OK +B.match?("^http://test.example.com") # $ Alert // NOT OK diff --git a/ruby/ql/test/query-tests/security/cwe-020/IncompleteUrlSubstringSanitization/IncompleteUrlSubstringSanitization.qlref b/ruby/ql/test/query-tests/security/cwe-020/IncompleteUrlSubstringSanitization/IncompleteUrlSubstringSanitization.qlref index dea02dce153..077f367fe47 100644 --- a/ruby/ql/test/query-tests/security/cwe-020/IncompleteUrlSubstringSanitization/IncompleteUrlSubstringSanitization.qlref +++ b/ruby/ql/test/query-tests/security/cwe-020/IncompleteUrlSubstringSanitization/IncompleteUrlSubstringSanitization.qlref @@ -1 +1,2 @@ -queries/security/cwe-020/IncompleteUrlSubstringSanitization.ql +query: queries/security/cwe-020/IncompleteUrlSubstringSanitization.ql +postprocess: utils/test/InlineExpectationsTestQuery.ql diff --git a/ruby/ql/test/query-tests/security/cwe-020/IncompleteUrlSubstringSanitization/tst-IncompleteUrlSubstringSanitization.rb b/ruby/ql/test/query-tests/security/cwe-020/IncompleteUrlSubstringSanitization/tst-IncompleteUrlSubstringSanitization.rb index dc6d49de57a..76a0a9ccdca 100644 --- a/ruby/ql/test/query-tests/security/cwe-020/IncompleteUrlSubstringSanitization/tst-IncompleteUrlSubstringSanitization.rb +++ b/ruby/ql/test/query-tests/security/cwe-020/IncompleteUrlSubstringSanitization/tst-IncompleteUrlSubstringSanitization.rb @@ -1,23 +1,23 @@ def test (x) - x.index("internal") != nil; # NOT OK, but not flagged - x.index("localhost") != nil; # NOT OK, but not flagged - x.index("secure.com") != nil; # NOT OK - x.index("secure.net") != nil; # NOT OK - x.index(".secure.com") != nil; # NOT OK - x.index("sub.secure.") != nil; # NOT OK, but not flagged - x.index(".sub.secure.") != nil; # NOT OK, but not flagged + x.index("internal") != nil; # $ MISSING: Alert # NOT OK, but not flagged + x.index("localhost") != nil; # $ MISSING: Alert # NOT OK, but not flagged + x.index("secure.com") != nil; # $ Alert // NOT OK + x.index("secure.net") != nil; # $ Alert // NOT OK + x.index(".secure.com") != nil; # $ Alert // NOT OK + x.index("sub.secure.") != nil; # $ MISSING: Alert # NOT OK, but not flagged + x.index(".sub.secure.") != nil; # $ MISSING: Alert # NOT OK, but not flagged - x.index("secure.com") === nil; # NOT OK - x.index("secure.com") === 0; # NOT OK - x.index("secure.com") >= 0; # NOT OK + x.index("secure.com") === nil; # $ Alert // NOT OK + x.index("secure.com") === 0; # $ Alert // NOT OK + x.index("secure.com") >= 0; # $ Alert // NOT OK - x.start_with?("https://secure.com"); # NOT OK - x.end_with?("secure.com"); # NOT OK + x.start_with?("https://secure.com"); # $ Alert // NOT OK + x.end_with?("secure.com"); # $ Alert // NOT OK x.end_with?(".secure.com"); # OK x.start_with?("secure.com/"); # OK x.index("secure.com/") === 0; # OK - x.include?("secure.com"); # NOT OK + x.include?("secure.com"); # $ Alert // NOT OK x.index("#") != nil; # OK x.index(":") != nil; # OK @@ -29,11 +29,11 @@ def test (x) x.index("some/path") != nil; # OK x.index("/index.html") != nil; # OK x.index(":template:") != nil; # OK - x.index("https://secure.com") != nil; # NOT OK - x.index("https://secure.com:443") != nil; # NOT OK - x.index("https://secure.com/") != nil; # NOT OK + x.index("https://secure.com") != nil; # $ Alert // NOT OK + x.index("https://secure.com:443") != nil; # $ Alert // NOT OK + x.index("https://secure.com/") != nil; # $ Alert // NOT OK - x.index(".cn") != nil; # NOT OK, but not flagged + x.index(".cn") != nil; # $ MISSING: Alert # NOT OK, but not flagged x.index(".jpg") != nil; # OK x.index("index.html") != nil; # OK x.index("index.js") != nil; # OK @@ -43,34 +43,34 @@ def test (x) x.index("secure=true") != nil; # OK (query param) x.index("&auth=") != nil; # OK (query param) - x.index(getCurrentDomain()) != nil; # NOT OK, but not flagged - x.index(location.origin) != nil; # NOT OK, but not flagged + x.index(getCurrentDomain()) != nil; # $ MISSING: Alert # NOT OK, but not flagged + x.index(location.origin) != nil; # $ MISSING: Alert # NOT OK, but not flagged x.index("tar.gz") + offset; # OK x.index("tar.gz") - offset; # OK - x.index("https://example.internal") != nil; # NOT OK + x.index("https://example.internal") != nil; # $ Alert // NOT OK x.index("https://") != nil; # OK - x.start_with?("https://example.internal"); # NOT OK - x.index('https://example.internal.org') != 0; # NOT OK - x.index('https://example.internal.org') === 0; # NOT OK - x.end_with?("internal.com"); # NOT OK + x.start_with?("https://example.internal"); # $ Alert // NOT OK + x.index('https://example.internal.org') != 0; # $ Alert // NOT OK + x.index('https://example.internal.org') === 0; # $ Alert // NOT OK + x.end_with?("internal.com"); # $ Alert // NOT OK x.start_with?("https://example.internal:80"); # OK - x.index("secure.com") != nil; # NOT OK - x.index("secure.com") === nil; # OK - !(x.index("secure.com") != nil); # OK - !x.include?("secure.com"); # OK + x.index("secure.com") != nil; # $ Alert // NOT OK + x.index("secure.com") === nil; # $ Alert // OK + !(x.index("secure.com") != nil); # $ Alert // OK + !x.include?("secure.com"); # $ Alert // OK - if !x.include?("secure.com") # NOT OK + if !x.include?("secure.com") # $ Alert // NOT OK else doSomeThingWithTrustedURL(x); end - + x.start_with?("https://secure.com/foo/bar"); # OK - a forward slash after the domain makes prefix checks safe. - x.index("https://secure.com/foo/bar") >= 0 # NOT OK - the url can be anywhere in the string. - x.index("https://secure.com") >= 0 # NOT OK - x.index("https://secure.com/foo/bar-baz") >= 0 # NOT OK - the url can be anywhere in the string. + x.index("https://secure.com/foo/bar") >= 0 # $ Alert // NOT OK - the url can be anywhere in the string. + x.index("https://secure.com") >= 0 # $ Alert // NOT OK + x.index("https://secure.com/foo/bar-baz") >= 0 # $ Alert // NOT OK - the url can be anywhere in the string. end diff --git a/ruby/ql/test/query-tests/security/cwe-020/MissingFullAnchor/MissingFullAnchor.qlref b/ruby/ql/test/query-tests/security/cwe-020/MissingFullAnchor/MissingFullAnchor.qlref index 4b61fcc56d7..8de0d5036bb 100644 --- a/ruby/ql/test/query-tests/security/cwe-020/MissingFullAnchor/MissingFullAnchor.qlref +++ b/ruby/ql/test/query-tests/security/cwe-020/MissingFullAnchor/MissingFullAnchor.qlref @@ -1 +1,2 @@ -queries/security/cwe-020/MissingFullAnchor.ql \ No newline at end of file +query: queries/security/cwe-020/MissingFullAnchor.ql +postprocess: utils/test/InlineExpectationsTestQuery.ql diff --git a/ruby/ql/test/query-tests/security/cwe-020/MissingFullAnchor/impl/miss-anchor.rb b/ruby/ql/test/query-tests/security/cwe-020/MissingFullAnchor/impl/miss-anchor.rb index c488990062a..04c09a7d786 100644 --- a/ruby/ql/test/query-tests/security/cwe-020/MissingFullAnchor/impl/miss-anchor.rb +++ b/ruby/ql/test/query-tests/security/cwe-020/MissingFullAnchor/impl/miss-anchor.rb @@ -1,17 +1,17 @@ class Foobar - def foo1(name) - raise Blabity, 'Invalid thing' if name !~ /^[A-Za-z0-9\+\-_]+(\/[A-Za-z0-9\+\-_]+)*$/ # NOT OK + def foo1(name) # $ Source + raise Blabity, 'Invalid thing' if name !~ /^[A-Za-z0-9\+\-_]+(\/[A-Za-z0-9\+\-_]+)*$/ # $ Alert // NOT OK end - def foo2(name) - raise Blabity, 'Invalid thing' unless name !~ /^[A-Za-z0-9\+\-_]+(\/[A-Za-z0-9\+\-_]+)*$/ # NOT OK + def foo2(name) # $ Source + raise Blabity, 'Invalid thing' unless name !~ /^[A-Za-z0-9\+\-_]+(\/[A-Za-z0-9\+\-_]+)*$/ # $ Alert // NOT OK end def foo3(name) raise Blabity, 'Invalid thing' unless name !~ /\A[A-Za-z0-9\+\-_]+(\/[A-Za-z0-9\+\-_]+)*\z/ # OK end - def foo4(name) - raise Blabity, 'Invalid thing' unless not name !~ /^[A-Za-z0-9\+\-_]+(\/[A-Za-z0-9\+\-_]+)*$/ # NOT OK + def foo4(name) # $ Source + raise Blabity, 'Invalid thing' unless not name !~ /^[A-Za-z0-9\+\-_]+(\/[A-Za-z0-9\+\-_]+)*$/ # $ Alert // NOT OK end end diff --git a/ruby/ql/test/query-tests/security/cwe-020/MissingRegExpAnchor/MissingRegExpAnchor.qlref b/ruby/ql/test/query-tests/security/cwe-020/MissingRegExpAnchor/MissingRegExpAnchor.qlref index bd3ad563aec..ffb6ae961f6 100644 --- a/ruby/ql/test/query-tests/security/cwe-020/MissingRegExpAnchor/MissingRegExpAnchor.qlref +++ b/ruby/ql/test/query-tests/security/cwe-020/MissingRegExpAnchor/MissingRegExpAnchor.qlref @@ -1 +1,2 @@ -queries/security/cwe-020/MissingRegExpAnchor.ql \ No newline at end of file +query: queries/security/cwe-020/MissingRegExpAnchor.ql +postprocess: utils/test/InlineExpectationsTestQuery.ql diff --git a/ruby/ql/test/query-tests/security/cwe-020/MissingRegExpAnchor/missing_regexp_anchor.rb b/ruby/ql/test/query-tests/security/cwe-020/MissingRegExpAnchor/missing_regexp_anchor.rb index 11410d7db1f..29d347269ac 100644 --- a/ruby/ql/test/query-tests/security/cwe-020/MissingRegExpAnchor/missing_regexp_anchor.rb +++ b/ruby/ql/test/query-tests/security/cwe-020/MissingRegExpAnchor/missing_regexp_anchor.rb @@ -1,11 +1,11 @@ -/www\.example\.com/ # BAD +/www\.example\.com/ # $ Alert // BAD /^www\.example\.com$/ # BAD: uses end-of-line anchors rather than end-of-string anchors /\Awww\.example\.com\z/ # GOOD /foo\.bar/ # GOOD -/https?:\/\/good\.com/ # BAD -/^https?:\/\/good\.com/ # BAD: missing end-of-string anchor +/https?:\/\/good\.com/ # $ Alert // BAD +/^https?:\/\/good\.com/ # $ Alert // BAD: missing end-of-string anchor /(^https?:\/\/good1\.com)|(^https?:#good2\.com)/ # BAD: missing end-of-string anchor /bar/ # GOOD @@ -16,40 +16,40 @@ foo.gsub!(/www\.example\.com/, "bar") # GOOD foo.sub!(/www\.example\.com/, "bar") # GOOD /^a|/ -/^a|b/ # BAD +/^a|b/ # $ Alert // BAD /a|^b/ /^a|^b/ -/^a|b|c/ # BAD +/^a|b|c/ # $ Alert // BAD /a|^b|c/ /a|b|^c/ /^a|^b|c/ /(^a)|b/ -/^a|(b)/ # BAD +/^a|(b)/ # $ Alert // BAD /^a|(^b)/ -/^(a)|(b)/ # BAD +/^(a)|(b)/ # $ Alert // BAD -/a|b$/ # BAD +/a|b$/ # $ Alert // BAD /a$|b/ /a$|b$/ -/a|b|c$/ # BAD +/a|b|c$/ # $ Alert // BAD /a|b$|c/ /a$|b|c/ /a|b$|c$/ /a|(b$)/ -/(a)|b$/ # BAD +/(a)|b$/ # $ Alert // BAD /(a$)|b$/ -/(a)|(b)$/ # BAD +/(a)|(b)$/ # $ Alert // BAD -/^good.com|better.com/ # BAD -/^good\.com|better\.com/ # BAD -/^good\\.com|better\\.com/ # BAD -/^good\\\.com|better\\\.com/ # BAD -/^good\\\\.com|better\\\\.com/ # BAD +/^good.com|better.com/ # $ Alert // BAD +/^good\.com|better\.com/ # $ Alert // BAD +/^good\\.com|better\\.com/ # $ Alert // BAD +/^good\\\.com|better\\\.com/ # $ Alert // BAD +/^good\\\\.com|better\\\\.com/ # $ Alert // BAD -/^foo|bar|baz$/ # BAD +/^foo|bar|baz$/ # $ Alert // BAD /^foo|%/ # OK REGEXP = /foo/ @@ -57,5 +57,5 @@ REGEXP.match? "http://example.com" # GOOD: the url is the text not the regexp REGEXP.match "http://example.com" # GOOD: the url is the text not the regexp "http://example.com".match? REGEXP # GOOD: the url is the text not the regexp "http://example.com".match REGEXP # GOOD: the url is the text not the regexp -"some text".match? "http://example.com" # BAD -"some text".match "http://example.com" # BAD +"some text".match? "http://example.com" # $ Alert // BAD +"some text".match "http://example.com" # $ Alert // BAD diff --git a/ruby/ql/test/query-tests/security/cwe-020/SuspiciousRegexpRange/OverlyLargeRangeQuery.qlref b/ruby/ql/test/query-tests/security/cwe-020/SuspiciousRegexpRange/OverlyLargeRangeQuery.qlref index f1d6eea73c2..476daefd7f3 100644 --- a/ruby/ql/test/query-tests/security/cwe-020/SuspiciousRegexpRange/OverlyLargeRangeQuery.qlref +++ b/ruby/ql/test/query-tests/security/cwe-020/SuspiciousRegexpRange/OverlyLargeRangeQuery.qlref @@ -1 +1,2 @@ -queries/security/cwe-020/OverlyLargeRange.ql +query: queries/security/cwe-020/OverlyLargeRange.ql +postprocess: utils/test/InlineExpectationsTestQuery.ql diff --git a/ruby/ql/test/query-tests/security/cwe-020/SuspiciousRegexpRange/suspicous_regexp_range.rb b/ruby/ql/test/query-tests/security/cwe-020/SuspiciousRegexpRange/suspicous_regexp_range.rb index ed6ffe21b14..57b5c3bee32 100644 --- a/ruby/ql/test/query-tests/security/cwe-020/SuspiciousRegexpRange/suspicous_regexp_range.rb +++ b/ruby/ql/test/query-tests/security/cwe-020/SuspiciousRegexpRange/suspicous_regexp_range.rb @@ -1,8 +1,8 @@ -overlap1 = /^[0-93-5]$/ # NOT OK +overlap1 = /^[0-93-5]$/ # $ Alert // NOT OK -overlap2 = /[A-ZA-z]/ # NOT OK +overlap2 = /[A-ZA-z]/ # $ Alert // NOT OK -isEmpty = /^[z-a]$/ # NOT OK +isEmpty = /^[z-a]$/ # $ Alert // NOT OK isAscii = /^[\x00-\x7F]*$/ # OK @@ -12,22 +12,22 @@ codePoints = /[^\x21-\x7E]|[\[\](){}<>\/%]/ # OK NON_ALPHANUMERIC_REGEXP = /([^\#-~| |!])/ # OK -smallOverlap = /[0-9a-fA-f]/ # NOT OK +smallOverlap = /[0-9a-fA-f]/ # $ Alert // NOT OK -weirdRange = /[$-`]/ # NOT OK +weirdRange = /[$-`]/ # $ Alert // NOT OK -keywordOperator = /[!\~\*\/%+-<>\^|=&]/ # NOT OK +keywordOperator = /[!\~\*\/%+-<>\^|=&]/ # $ Alert // NOT OK -notYoutube = /youtu\.be\/[a-z1-9.-_]+/ # NOT OK +notYoutube = /youtu\.be\/[a-z1-9.-_]+/ # $ Alert // NOT OK -numberToLetter = /[7-F]/ # NOT OK +numberToLetter = /[7-F]/ # $ Alert // NOT OK -overlapsWithClass1 = /[0-9\d]/ # NOT OK +overlapsWithClass1 = /[0-9\d]/ # $ Alert // NOT OK -overlapsWithClass2 = /[\w,.-?:*+]/ # NOT OK +overlapsWithClass2 = /[\w,.-?:*+]/ # $ Alert // NOT OK escapes = /[\000-\037\047\134\177-\377]/n # OK - they are escapes nested = /[a-z&&[^a-c]]/ # OK -overlapsWithNothing = /[\w_%-.]/; \ No newline at end of file +overlapsWithNothing = /[\w_%-.]/; # $ Alert diff --git a/ruby/ql/test/query-tests/security/cwe-078/KernelOpen/KernelOpen.qlref b/ruby/ql/test/query-tests/security/cwe-078/KernelOpen/KernelOpen.qlref index aea01648c78..b8b59265f26 100644 --- a/ruby/ql/test/query-tests/security/cwe-078/KernelOpen/KernelOpen.qlref +++ b/ruby/ql/test/query-tests/security/cwe-078/KernelOpen/KernelOpen.qlref @@ -1 +1,2 @@ -queries/security/cwe-078/KernelOpen.ql \ No newline at end of file +query: queries/security/cwe-078/KernelOpen.ql +postprocess: utils/test/InlineExpectationsTestQuery.ql diff --git a/ruby/ql/test/query-tests/security/cwe-078/KernelOpen/KernelOpen.rb b/ruby/ql/test/query-tests/security/cwe-078/KernelOpen/KernelOpen.rb index 412e2c50ead..ca8d4aee192 100644 --- a/ruby/ql/test/query-tests/security/cwe-078/KernelOpen/KernelOpen.rb +++ b/ruby/ql/test/query-tests/security/cwe-078/KernelOpen/KernelOpen.rb @@ -1,16 +1,16 @@ class UsersController < ActionController::Base def create - file = params[:file] - open(file) # BAD - IO.read(file) # BAD - IO.write(file) # BAD - IO.binread(file) # BAD - IO.binwrite(file) # BAD - IO.foreach(file) # BAD - IO.readlines(file) # BAD - URI.open(file) # BAD + file = params[:file] # $ Source + open(file) # $ Alert // BAD + IO.read(file) # $ Alert // BAD + IO.write(file) # $ Alert // BAD + IO.binread(file) # $ Alert // BAD + IO.binwrite(file) # $ Alert // BAD + IO.foreach(file) # $ Alert // BAD + IO.readlines(file) # $ Alert // BAD + URI.open(file) # $ Alert // BAD - IO.read(File.join(file, "")) # BAD - file as first argument to File.join + IO.read(File.join(file, "")) # $ Alert // BAD - file as first argument to File.join IO.read(File.join("", file)) # GOOD - file path is sanitised by guard File.open(file).read # GOOD @@ -23,6 +23,6 @@ class UsersController < ActionController::Base IO.read(file) # GOOD - file path is sanitised by guard end - open(file) # BAD - sanity check to verify that file was not mistakenly marked as sanitized + open(file) # $ Alert // BAD - sanity check to verify that file was not mistakenly marked as sanitized end end diff --git a/ruby/ql/test/query-tests/security/cwe-078/NonConstantKernelOpen/NonConstantKernelOpen.qlref b/ruby/ql/test/query-tests/security/cwe-078/NonConstantKernelOpen/NonConstantKernelOpen.qlref index 0b23d9102b9..7b559b55ae0 100644 --- a/ruby/ql/test/query-tests/security/cwe-078/NonConstantKernelOpen/NonConstantKernelOpen.qlref +++ b/ruby/ql/test/query-tests/security/cwe-078/NonConstantKernelOpen/NonConstantKernelOpen.qlref @@ -1 +1,2 @@ -queries/security/cwe-078/NonConstantKernelOpen.ql \ No newline at end of file +query: queries/security/cwe-078/NonConstantKernelOpen.ql +postprocess: utils/test/InlineExpectationsTestQuery.ql diff --git a/ruby/ql/test/query-tests/security/cwe-078/NonConstantKernelOpen/NonConstantKernelOpen.rb b/ruby/ql/test/query-tests/security/cwe-078/NonConstantKernelOpen/NonConstantKernelOpen.rb index 6b8294fa111..4283fd4c969 100644 --- a/ruby/ql/test/query-tests/security/cwe-078/NonConstantKernelOpen/NonConstantKernelOpen.rb +++ b/ruby/ql/test/query-tests/security/cwe-078/NonConstantKernelOpen/NonConstantKernelOpen.rb @@ -4,18 +4,18 @@ class UsersController < ActionController::Base def create file = params[:file] - open(file) # BAD - IO.read(file) # BAD - IO.write(file) # BAD - IO.binread(file) # BAD - IO.binwrite(file) # BAD - IO.foreach(file) # BAD - IO.readlines(file) # BAD - URI.open(file) # BAD + open(file) # $ Alert // BAD + IO.read(file) # $ Alert // BAD + IO.write(file) # $ Alert // BAD + IO.binread(file) # $ Alert // BAD + IO.binwrite(file) # $ Alert // BAD + IO.foreach(file) # $ Alert // BAD + IO.readlines(file) # $ Alert // BAD + URI.open(file) # $ Alert // BAD File.open(file).read # GOOD - Kernel.open(file) # BAD + Kernel.open(file) # $ Alert // BAD File.open(file, "r") # GOOD @@ -25,7 +25,7 @@ class UsersController < ActionController::Base Kernel.open("this is #{fine}") # GOOD - Kernel.open("#{this_is} bad") # BAD + Kernel.open("#{this_is} bad") # $ Alert // BAD open("| #{this_is_an_explicit_command} foo bar") # GOOD @@ -43,6 +43,6 @@ class UsersController < ActionController::Base open.where(external: false) # GOOD - an open method is called withoout arguments - open(file) # BAD - sanity check to verify that file was not mistakenly marked as sanitized + open(file) # $ Alert // BAD - sanity check to verify that file was not mistakenly marked as sanitized end end diff --git a/ruby/ql/test/query-tests/security/cwe-078/UnsafeShellCommandConstruction/UnsafeShellCommandConstruction.qlref b/ruby/ql/test/query-tests/security/cwe-078/UnsafeShellCommandConstruction/UnsafeShellCommandConstruction.qlref index 99292da7663..da9659dee16 100644 --- a/ruby/ql/test/query-tests/security/cwe-078/UnsafeShellCommandConstruction/UnsafeShellCommandConstruction.qlref +++ b/ruby/ql/test/query-tests/security/cwe-078/UnsafeShellCommandConstruction/UnsafeShellCommandConstruction.qlref @@ -1 +1,2 @@ -queries/security/cwe-078/UnsafeShellCommandConstruction.ql \ No newline at end of file +query: queries/security/cwe-078/UnsafeShellCommandConstruction.ql +postprocess: utils/test/InlineExpectationsTestQuery.ql diff --git a/ruby/ql/test/query-tests/security/cwe-078/UnsafeShellCommandConstruction/impl/sub/notImported.rb b/ruby/ql/test/query-tests/security/cwe-078/UnsafeShellCommandConstruction/impl/sub/notImported.rb index 0a385f5f6bc..d5f03d94b5a 100644 --- a/ruby/ql/test/query-tests/security/cwe-078/UnsafeShellCommandConstruction/impl/sub/notImported.rb +++ b/ruby/ql/test/query-tests/security/cwe-078/UnsafeShellCommandConstruction/impl/sub/notImported.rb @@ -1,6 +1,5 @@ class Foobar - def foo1(target) - IO.popen("cat #{target}", "w") # NOT OK - everything assumed to be imported... + def foo1(target) # $ Source + IO.popen("cat #{target}", "w") # $ Alert // NOT OK - everything assumed to be imported... end end - \ No newline at end of file diff --git a/ruby/ql/test/query-tests/security/cwe-078/UnsafeShellCommandConstruction/impl/sub/other.rb b/ruby/ql/test/query-tests/security/cwe-078/UnsafeShellCommandConstruction/impl/sub/other.rb index 22eaa13bcc0..29d1b95e3fb 100644 --- a/ruby/ql/test/query-tests/security/cwe-078/UnsafeShellCommandConstruction/impl/sub/other.rb +++ b/ruby/ql/test/query-tests/security/cwe-078/UnsafeShellCommandConstruction/impl/sub/other.rb @@ -1,7 +1,7 @@ class Foobar - def foo1(target) - IO.popen("cat #{target}", "w") # NOT OK + def foo1(target) # $ Source + IO.popen("cat #{target}", "w") # $ Alert // NOT OK end end -require 'sub/other2' \ No newline at end of file +require 'sub/other2' diff --git a/ruby/ql/test/query-tests/security/cwe-078/UnsafeShellCommandConstruction/impl/sub/other2.rb b/ruby/ql/test/query-tests/security/cwe-078/UnsafeShellCommandConstruction/impl/sub/other2.rb index 007dae343ff..76deb5234b8 100644 --- a/ruby/ql/test/query-tests/security/cwe-078/UnsafeShellCommandConstruction/impl/sub/other2.rb +++ b/ruby/ql/test/query-tests/security/cwe-078/UnsafeShellCommandConstruction/impl/sub/other2.rb @@ -1,5 +1,5 @@ class Foobar - def foo1(target) - IO.popen("cat #{target}", "w") # NOT OK + def foo1(target) # $ Source + IO.popen("cat #{target}", "w") # $ Alert // NOT OK end -end \ No newline at end of file +end diff --git a/ruby/ql/test/query-tests/security/cwe-078/UnsafeShellCommandConstruction/impl/unsafeShell.rb b/ruby/ql/test/query-tests/security/cwe-078/UnsafeShellCommandConstruction/impl/unsafeShell.rb index 487ca06ebd6..a2c3cfe38ca 100644 --- a/ruby/ql/test/query-tests/security/cwe-078/UnsafeShellCommandConstruction/impl/unsafeShell.rb +++ b/ruby/ql/test/query-tests/security/cwe-078/UnsafeShellCommandConstruction/impl/unsafeShell.rb @@ -1,10 +1,10 @@ class Foobar - def foo1(target) - IO.popen("cat #{target}", "w") # NOT OK + def foo1(target) # $ Source + IO.popen("cat #{target}", "w") # $ Alert // NOT OK end - def foo2(x) - format = sprintf("cat %s", x) # NOT OK + def foo2(x) # $ Source + format = sprintf("cat %s", x) # $ Alert // NOT OK IO.popen(format, "w") end @@ -12,30 +12,30 @@ class Foobar File.read(path) # OK end - def my_exec(cmd, command, myCmd, myCommand, innocent_file_path) + def my_exec(cmd, command, myCmd, myCommand, innocent_file_path) # $ Source IO.popen("which #{cmd}", "w") # OK - the parameter is named `cmd`, so it's meant to be a command IO.popen("which #{command}", "w") # OK - the parameter is named `command`, so it's meant to be a command IO.popen("which #{myCmd}", "w") # OK - the parameter is named `myCmd`, so it's meant to be a command IO.popen("which #{myCommand}", "w") # OK - the parameter is named `myCommand`, so it's meant to be a command - IO.popen("which #{innocent_file_path}", "w") # NOT OK - the parameter is named `innocent_file_path`, so it's not meant to be a command + IO.popen("which #{innocent_file_path}", "w") # $ Alert // NOT OK - the parameter is named `innocent_file_path`, so it's not meant to be a command end - def escaped(file_path) + def escaped(file_path) # $ Source IO.popen("cat #{file_path.shellescape}", "w") # OK - the parameter is escaped - IO.popen("cat #{file_path}", "w") # NOT OK - the parameter is not escaped + IO.popen("cat #{file_path}", "w") # $ Alert // NOT OK - the parameter is not escaped end end require File.join(File.dirname(__FILE__), 'sub', 'other') class Foobar2 - def foo1(target) - IO.popen("cat #{target}", "w") # NOT OK + def foo1(target) # $ Source + IO.popen("cat #{target}", "w") # $ Alert // NOT OK end - def id(x) - IO.popen("cat #{x}", "w") # NOT OK - the parameter is not a constant. + def id(x) # $ Source + IO.popen("cat #{x}", "w") # $ Alert // NOT OK - the parameter is not a constant. return x end @@ -44,27 +44,27 @@ class Foobar2 end # class methods - def self.foo(target) - IO.popen("cat #{target}", "w") # NOT OK + def self.foo(target) # $ Source + IO.popen("cat #{target}", "w") # $ Alert // NOT OK end - def arrayJoin(x) - IO.popen(x.join(' '), "w") # NOT OK + def arrayJoin(x) # $ Source + IO.popen(x.join(' '), "w") # $ Alert // NOT OK - IO.popen(["foo", "bar", x].join(' '), "w") # NOT OK + IO.popen(["foo", "bar", x].join(' '), "w") # $ Alert // NOT OK end - def string_concat(x) - IO.popen("cat " + x, "w") # NOT OK + def string_concat(x) # $ Source + IO.popen("cat " + x, "w") # $ Alert // NOT OK end - def array_taint (x, y) + def array_taint (x, y) # $ Source arr = ["cat"] arr.push(x) - IO.popen(arr.join(' '), "w") # NOT OK + IO.popen(arr.join(' '), "w") # $ Alert // NOT OK arr2 = ["cat"] arr2 << y - IO.popen(arr.join(' '), "w") # NOT OK + IO.popen(arr.join(' '), "w") # $ Alert // NOT OK end end diff --git a/ruby/ql/test/query-tests/security/cwe-089/ActiveRecordInjection.rb b/ruby/ql/test/query-tests/security/cwe-089/ActiveRecordInjection.rb index 3a782e529d5..6696f578cbc 100644 --- a/ruby/ql/test/query-tests/security/cwe-089/ActiveRecordInjection.rb +++ b/ruby/ql/test/query-tests/security/cwe-089/ActiveRecordInjection.rb @@ -7,13 +7,13 @@ class User < ApplicationRecord def self.authenticate(name, pass) # BAD: possible untrusted input interpolated into SQL fragment - find(:first, :conditions => "name='#{name}' and pass='#{pass}'") + find(:first, :conditions => "name='#{name}' and pass='#{pass}'") # $ Alert # BAD: interpolation in array argument - find(:first, conditions: ["name='#{name}' and pass='#{pass}'"]) + find(:first, conditions: ["name='#{name}' and pass='#{pass}'"]) # $ Alert # GOOD: using SQL parameters find(:first, conditions: ["name = ? and pass = ?", name, pass]) # BAD: interpolation with flow - conds = "name=#{name}" + conds = "name=#{name}" # $ Alert find(:first, conditions: conds) end @@ -27,7 +27,7 @@ class Admin < User def self.delete_by(condition = nil) # BAD: `delete_by overrides an ActiveRecord method, but doesn't perform # any validation before passing its arguments on to another ActiveRecord method - destroy_by(condition) + destroy_by(condition) # $ Alert end end @@ -39,64 +39,65 @@ class FooController < ActionController::Base def some_request_handler # BAD: executes `SELECT AVG(#{params[:column]}) FROM "users"` # where `params[:column]` is unsanitized - User.calculate(:average, params[:column]) + User.calculate(:average, params[:column]) # $ Alert # BAD: executes `SELECT MAX(#{params[:column]}) FROM "users"` # where `params[:column]` is unsanitized - User.maximum(params[:column]) + User.maximum(params[:column]) # $ Alert # BAD: executes `DELETE FROM "users" WHERE (id = '#{params[:id]}')` # where `params[:id]` is unsanitized - User.delete_by("id = '#{params[:id]}'") + User.delete_by("id = '#{params[:id]}'") # $ Alert # BAD: executes `DELETE FROM "users" WHERE (id = '#{params[:id]}')` # where `params[:id]` is unsanitized # (in Rails < 4.0) - User.delete_all("id = '#{params[:id]}'") + User.delete_all("id = '#{params[:id]}'") # $ Alert # BAD: executes `SELECT "users".* FROM "users" WHERE (id = '#{params[:id]}')` # where `params[:id]` is unsanitized - User.destroy_by(["id = '#{params[:id]}'"]) + User.destroy_by(["id = '#{params[:id]}'"]) # $ Alert # BAD: executes `SELECT "users".* FROM "users" WHERE (id = '#{params[:id]}')` # where `params[:id]` is unsanitized # (in Rails < 4.0) - User.destroy_all(["id = '#{params[:id]}'"]) + User.destroy_all(["id = '#{params[:id]}'"]) # $ Alert # BAD: executes `SELECT "users".* FROM "users" WHERE id BETWEEN '#{params[:min_id]}' AND 100000` # where `params[:min_id]` is unsanitized - User.where(<<-SQL, MAX_USER_ID) - id BETWEEN '#{params[:min_id]}' AND ? + User.where(<<-SQL, MAX_USER_ID) # $ Alert + id BETWEEN '#{params[:min_id]}' AND ? #{# $ Source + } SQL # BAD: chained method case # executes `SELECT "users".* FROM "users" WHERE (NOT (user_id = 'params[:id]'))` # where `params[:id]` is unsanitized - User.where.not("user.id = '#{params[:id]}'") + User.where.not("user.id = '#{params[:id]}'") # $ Alert - User.authenticate(params[:name], params[:pass]) + User.authenticate(params[:name], params[:pass]) # $ Source # BAD: executes `SELECT "users".* FROM "users" WHERE (id = '#{params[:id]}')` LIMIT 1 # where `params[:id]` is unsanitized - User.find_or_initialize_by("id = '#{params[:id]}'") + User.find_or_initialize_by("id = '#{params[:id]}'") # $ Alert user = User.first # BAD: executes `SELECT "users".* FROM "users" WHERE id = 1 LIMIT 1 #{params[:lock]}` # where `params[:lock]` is unsanitized - user.reload(lock: params[:lock]) + user.reload(lock: params[:lock]) # $ Alert # BAD: executes `SELECT #{params[:column]} FROM "users"` # where `params[:column]` is unsanitized - User.select(params[:column]) - User.reselect(params[:column]) + User.select(params[:column]) # $ Alert + User.reselect(params[:column]) # $ Alert # BAD: executes `SELECT "users".* FROM "users" WHERE (#{params[:condition]})` # where `params[:condition]` is unsanitized - User.rewhere(params[:condition]) + User.rewhere(params[:condition]) # $ Alert # BAD: executes `UPDATE "users" SET #{params[:fields]}` # where `params[:fields]` is unsanitized - User.update_all(params[:fields]) + User.update_all(params[:fields]) # $ Alert # GOOD -- `update_all` sanitizes its bind variable arguments User.find_by(name: params[:user_name]) @@ -104,41 +105,41 @@ class FooController < ActionController::Base # BAD -- `update_all` does not sanitize its query (array arg) User.find_by(name: params[:user_name]) - .update_all(["name = '#{params[:new_user_name]}'"]) + .update_all(["name = '#{params[:new_user_name]}'"]) # $ Alert # BAD -- `update_all` does not sanitize its query (string arg) User.find_by(name: params[:user_name]) - .update_all("name = '#{params[:new_user_name]}'") + .update_all("name = '#{params[:new_user_name]}'") # $ Alert - User.reorder(params[:direction]) + User.reorder(params[:direction]) # $ Alert - User.select('a','b', params[:column]) - User.reselect('a','b', params[:column]) - User.order('a ASC', "b #{params[:direction]}") - User.reorder('a ASC', "b #{params[:direction]}") - User.group('a', params[:column]) - User.pluck('a', params[:column]) - User.joins(:a, params[:column]) + User.select('a','b', params[:column]) # $ Alert + User.reselect('a','b', params[:column]) # $ Alert + User.order('a ASC', "b #{params[:direction]}") # $ Alert + User.reorder('a ASC', "b #{params[:direction]}") # $ Alert + User.group('a', params[:column]) # $ Alert + User.pluck('a', params[:column]) # $ Alert + User.joins(:a, params[:column]) # $ Alert - User.count_by_sql(params[:custom_sql_query]) + User.count_by_sql(params[:custom_sql_query]) # $ Alert # BAD: executes `SELECT users.* FROM #{params[:tab]}` # where `params[:tab]` is unsanitized - User.all.from(params[:tab]) + User.all.from(params[:tab]) # $ Alert # BAD: executes `SELECT "users".* FROM (SELECT "users".* FROM "users") #{params[:sq]} - User.all.from(User.all, params[:sq]) + User.all.from(User.all, params[:sq]) # $ Alert end end class BarController < ApplicationController def some_other_request_handler - ps = params + ps = params # $ Source uid = ps[:id] uidEq = "= '#{uid}'" # BAD: executes `DELETE FROM "users" WHERE (id = #{uid})` # where `uid` is unsantized - User.delete_by("id " + uidEq) + User.delete_by("id " + uidEq) # $ Alert end def safe_paths @@ -171,7 +172,7 @@ end class BazController < BarController def yet_another_handler - Admin.delete_by(params[:admin_condition]) + Admin.delete_by(params[:admin_condition]) # $ Alert Source end end @@ -185,7 +186,7 @@ class AnnotatedController < ActionController::Base def unsafe_action name = params[:user_name] # BAD: user input passed into annotations are vulnerable to SQLi - users = User.annotate("this is an unsafe annotation:#{params[:comment]}").find_by(user_name: name) + users = User.annotate("this is an unsafe annotation:#{params[:comment]}").find_by(user_name: name) # $ Alert end end @@ -198,27 +199,27 @@ class RegressionController < ActionController::Base def index my_params = permitted_params query = "SELECT * FROM users WHERE id = #{my_params[:user_id]}" - result = Regression.find_by_sql(query) + result = Regression.find_by_sql(query) # $ Alert end def permitted_params - params.require(:my_key).permit(:id, :user_id, :my_type) + params.require(:my_key).permit(:id, :user_id, :my_type) # $ Source end def show - ActiveRecord::Base.connection.execute("SELECT * FROM users WHERE id = #{permitted_params[:user_id]}") - Regression.connection.execute("SELECT * FROM users WHERE id = #{permitted_params[:user_id]}") + ActiveRecord::Base.connection.execute("SELECT * FROM users WHERE id = #{permitted_params[:user_id]}") # $ Alert + Regression.connection.execute("SELECT * FROM users WHERE id = #{permitted_params[:user_id]}") # $ Alert end end class User - scope :with_role, ->(role) { where("role = #{role}") } + scope :with_role, ->(role) { where("role = #{role}") } # $ Alert end class UsersController < ActionController::Base def index # BAD: user input passed to scope which uses it without sanitization. - @users = User.with_role(params[:role]) + @users = User.with_role(params[:role]) # $ Source end end diff --git a/ruby/ql/test/query-tests/security/cwe-089/ArelInjection.rb b/ruby/ql/test/query-tests/security/cwe-089/ArelInjection.rb index 1cd6782b241..526970c138e 100644 --- a/ruby/ql/test/query-tests/security/cwe-089/ArelInjection.rb +++ b/ruby/ql/test/query-tests/security/cwe-089/ArelInjection.rb @@ -1,9 +1,9 @@ class PotatoController < ActionController::Base def unsafe_action - name = params[:user_name] + name = params[:user_name] # $ Source # BAD: SQL statement constructed from user input - sql = Arel.sql("SELECT * FROM users WHERE name = #{name}") - sql = Arel::Nodes::SqlLiteral.new("SELECT * FROM users WHERE name = #{name}") + sql = Arel.sql("SELECT * FROM users WHERE name = #{name}") # $ Alert + sql = Arel::Nodes::SqlLiteral.new("SELECT * FROM users WHERE name = #{name}") # $ Alert end -end \ No newline at end of file +end diff --git a/ruby/ql/test/query-tests/security/cwe-089/PgInjection.rb b/ruby/ql/test/query-tests/security/cwe-089/PgInjection.rb index 549be489858..c44e078ee84 100644 --- a/ruby/ql/test/query-tests/security/cwe-089/PgInjection.rb +++ b/ruby/ql/test/query-tests/security/cwe-089/PgInjection.rb @@ -3,7 +3,7 @@ class FooController < ActionController::Base def some_request_handler # A string tainted by user input is inserted into a query # (i.e a remote flow source) - name = params[:name] + name = params[:name] # $ Source # Establish a connection to a PostgreSQL database conn = PG::Connection.open(:dbname => 'postgresql', :user => 'user', :password => 'pass', :host => 'localhost', :port => '5432') @@ -11,14 +11,14 @@ class FooController < ActionController::Base # .exec() and .async_exec() # BAD: SQL statement constructed from user input qry1 = "SELECT * FROM users WHERE username = '#{name}';" - conn.exec(qry1) - conn.async_exec(qry1) + conn.exec(qry1) # $ Alert + conn.async_exec(qry1) # $ Alert # .exec_params() and .async_exec_params() # BAD: SQL statement constructed from user input qry2 = "SELECT * FROM users WHERE username = '#{name}';" - conn.exec_params(qry2) - conn.async_exec_params(qry2) + conn.exec_params(qry2) # $ Alert + conn.async_exec_params(qry2) # $ Alert # .exec_params() and .async_exec_params() # GOOD: SQL statement constructed from sanitized user input @@ -29,7 +29,7 @@ class FooController < ActionController::Base # .prepare() and .exec_prepared() # BAD: SQL statement constructed from user input qry3 = "SELECT * FROM users WHERE username = '#{name}';" - conn.prepare("query_1", qry3) + conn.prepare("query_1", qry3) # $ Alert conn.exec_prepared('query_1') # .prepare() and .exec_prepared() @@ -41,7 +41,7 @@ class FooController < ActionController::Base # .prepare() and .exec_prepared() # NOT EXECUTED: SQL statement constructed from user input but not executed qry3 = "SELECT * FROM users WHERE username = '#{name}';" - conn.prepare("query_3", qry3) + conn.prepare("query_3", qry3) # $ Alert end end diff --git a/ruby/ql/test/query-tests/security/cwe-089/SqlInjection.expected b/ruby/ql/test/query-tests/security/cwe-089/SqlInjection.expected index 069cb34810f..c8926f635c4 100644 --- a/ruby/ql/test/query-tests/security/cwe-089/SqlInjection.expected +++ b/ruby/ql/test/query-tests/security/cwe-089/SqlInjection.expected @@ -1,3 +1,52 @@ +#select +| ActiveRecordInjection.rb:10:33:10:67 | "name='#{...}' and pass='#{...}'" | ActiveRecordInjection.rb:78:23:78:28 | call to params | ActiveRecordInjection.rb:10:33:10:67 | "name='#{...}' and pass='#{...}'" | This SQL query depends on a $@. | ActiveRecordInjection.rb:78:23:78:28 | call to params | user-provided value | +| ActiveRecordInjection.rb:10:33:10:67 | "name='#{...}' and pass='#{...}'" | ActiveRecordInjection.rb:78:38:78:43 | call to params | ActiveRecordInjection.rb:10:33:10:67 | "name='#{...}' and pass='#{...}'" | This SQL query depends on a $@. | ActiveRecordInjection.rb:78:38:78:43 | call to params | user-provided value | +| ActiveRecordInjection.rb:12:31:12:65 | "name='#{...}' and pass='#{...}'" | ActiveRecordInjection.rb:78:23:78:28 | call to params | ActiveRecordInjection.rb:12:31:12:65 | "name='#{...}' and pass='#{...}'" | This SQL query depends on a $@. | ActiveRecordInjection.rb:78:23:78:28 | call to params | user-provided value | +| ActiveRecordInjection.rb:12:31:12:65 | "name='#{...}' and pass='#{...}'" | ActiveRecordInjection.rb:78:38:78:43 | call to params | ActiveRecordInjection.rb:12:31:12:65 | "name='#{...}' and pass='#{...}'" | This SQL query depends on a $@. | ActiveRecordInjection.rb:78:38:78:43 | call to params | user-provided value | +| ActiveRecordInjection.rb:16:13:16:26 | "name=#{...}" | ActiveRecordInjection.rb:78:23:78:28 | call to params | ActiveRecordInjection.rb:16:13:16:26 | "name=#{...}" | This SQL query depends on a $@. | ActiveRecordInjection.rb:78:23:78:28 | call to params | user-provided value | +| ActiveRecordInjection.rb:30:16:30:24 | condition | ActiveRecordInjection.rb:175:21:175:26 | call to params | ActiveRecordInjection.rb:30:16:30:24 | condition | This SQL query depends on a $@. | ActiveRecordInjection.rb:175:21:175:26 | call to params | user-provided value | +| ActiveRecordInjection.rb:42:30:42:44 | ...[...] | ActiveRecordInjection.rb:42:30:42:35 | call to params | ActiveRecordInjection.rb:42:30:42:44 | ...[...] | This SQL query depends on a $@. | ActiveRecordInjection.rb:42:30:42:35 | call to params | user-provided value | +| ActiveRecordInjection.rb:46:18:46:32 | ...[...] | ActiveRecordInjection.rb:46:18:46:23 | call to params | ActiveRecordInjection.rb:46:18:46:32 | ...[...] | This SQL query depends on a $@. | ActiveRecordInjection.rb:46:18:46:23 | call to params | user-provided value | +| ActiveRecordInjection.rb:50:20:50:42 | "id = '#{...}'" | ActiveRecordInjection.rb:50:29:50:34 | call to params | ActiveRecordInjection.rb:50:20:50:42 | "id = '#{...}'" | This SQL query depends on a $@. | ActiveRecordInjection.rb:50:29:50:34 | call to params | user-provided value | +| ActiveRecordInjection.rb:55:21:55:43 | "id = '#{...}'" | ActiveRecordInjection.rb:55:30:55:35 | call to params | ActiveRecordInjection.rb:55:21:55:43 | "id = '#{...}'" | This SQL query depends on a $@. | ActiveRecordInjection.rb:55:30:55:35 | call to params | user-provided value | +| ActiveRecordInjection.rb:59:21:59:45 | call to [] | ActiveRecordInjection.rb:59:31:59:36 | call to params | ActiveRecordInjection.rb:59:21:59:45 | call to [] | This SQL query depends on a $@. | ActiveRecordInjection.rb:59:31:59:36 | call to params | user-provided value | +| ActiveRecordInjection.rb:64:22:64:46 | call to [] | ActiveRecordInjection.rb:64:32:64:37 | call to params | ActiveRecordInjection.rb:64:22:64:46 | call to [] | This SQL query depends on a $@. | ActiveRecordInjection.rb:64:32:64:37 | call to params | user-provided value | +| ActiveRecordInjection.rb:68:16:68:21 | <<-SQL | ActiveRecordInjection.rb:69:21:69:26 | call to params | ActiveRecordInjection.rb:68:16:68:21 | <<-SQL | This SQL query depends on a $@. | ActiveRecordInjection.rb:69:21:69:26 | call to params | user-provided value | +| ActiveRecordInjection.rb:76:20:76:47 | "user.id = '#{...}'" | ActiveRecordInjection.rb:76:34:76:39 | call to params | ActiveRecordInjection.rb:76:20:76:47 | "user.id = '#{...}'" | This SQL query depends on a $@. | ActiveRecordInjection.rb:76:34:76:39 | call to params | user-provided value | +| ActiveRecordInjection.rb:82:32:82:54 | "id = '#{...}'" | ActiveRecordInjection.rb:82:41:82:46 | call to params | ActiveRecordInjection.rb:82:32:82:54 | "id = '#{...}'" | This SQL query depends on a $@. | ActiveRecordInjection.rb:82:41:82:46 | call to params | user-provided value | +| ActiveRecordInjection.rb:87:23:87:35 | ...[...] | ActiveRecordInjection.rb:87:23:87:28 | call to params | ActiveRecordInjection.rb:87:23:87:35 | ...[...] | This SQL query depends on a $@. | ActiveRecordInjection.rb:87:23:87:28 | call to params | user-provided value | +| ActiveRecordInjection.rb:91:17:91:31 | ...[...] | ActiveRecordInjection.rb:91:17:91:22 | call to params | ActiveRecordInjection.rb:91:17:91:31 | ...[...] | This SQL query depends on a $@. | ActiveRecordInjection.rb:91:17:91:22 | call to params | user-provided value | +| ActiveRecordInjection.rb:92:19:92:33 | ...[...] | ActiveRecordInjection.rb:92:19:92:24 | call to params | ActiveRecordInjection.rb:92:19:92:33 | ...[...] | This SQL query depends on a $@. | ActiveRecordInjection.rb:92:19:92:24 | call to params | user-provided value | +| ActiveRecordInjection.rb:96:18:96:35 | ...[...] | ActiveRecordInjection.rb:96:18:96:23 | call to params | ActiveRecordInjection.rb:96:18:96:35 | ...[...] | This SQL query depends on a $@. | ActiveRecordInjection.rb:96:18:96:23 | call to params | user-provided value | +| ActiveRecordInjection.rb:100:21:100:35 | ...[...] | ActiveRecordInjection.rb:100:21:100:26 | call to params | ActiveRecordInjection.rb:100:21:100:35 | ...[...] | This SQL query depends on a $@. | ActiveRecordInjection.rb:100:21:100:26 | call to params | user-provided value | +| ActiveRecordInjection.rb:108:20:108:55 | "name = '#{...}'" | ActiveRecordInjection.rb:108:31:108:36 | call to params | ActiveRecordInjection.rb:108:20:108:55 | "name = '#{...}'" | This SQL query depends on a $@. | ActiveRecordInjection.rb:108:31:108:36 | call to params | user-provided value | +| ActiveRecordInjection.rb:112:19:112:54 | "name = '#{...}'" | ActiveRecordInjection.rb:112:30:112:35 | call to params | ActiveRecordInjection.rb:112:19:112:54 | "name = '#{...}'" | This SQL query depends on a $@. | ActiveRecordInjection.rb:112:30:112:35 | call to params | user-provided value | +| ActiveRecordInjection.rb:114:18:114:35 | ...[...] | ActiveRecordInjection.rb:114:18:114:23 | call to params | ActiveRecordInjection.rb:114:18:114:35 | ...[...] | This SQL query depends on a $@. | ActiveRecordInjection.rb:114:18:114:23 | call to params | user-provided value | +| ActiveRecordInjection.rb:116:26:116:40 | ...[...] | ActiveRecordInjection.rb:116:26:116:31 | call to params | ActiveRecordInjection.rb:116:26:116:40 | ...[...] | This SQL query depends on a $@. | ActiveRecordInjection.rb:116:26:116:31 | call to params | user-provided value | +| ActiveRecordInjection.rb:117:28:117:42 | ...[...] | ActiveRecordInjection.rb:117:28:117:33 | call to params | ActiveRecordInjection.rb:117:28:117:42 | ...[...] | This SQL query depends on a $@. | ActiveRecordInjection.rb:117:28:117:33 | call to params | user-provided value | +| ActiveRecordInjection.rb:118:25:118:49 | "b #{...}" | ActiveRecordInjection.rb:118:30:118:35 | call to params | ActiveRecordInjection.rb:118:25:118:49 | "b #{...}" | This SQL query depends on a $@. | ActiveRecordInjection.rb:118:30:118:35 | call to params | user-provided value | +| ActiveRecordInjection.rb:119:27:119:51 | "b #{...}" | ActiveRecordInjection.rb:119:32:119:37 | call to params | ActiveRecordInjection.rb:119:27:119:51 | "b #{...}" | This SQL query depends on a $@. | ActiveRecordInjection.rb:119:32:119:37 | call to params | user-provided value | +| ActiveRecordInjection.rb:120:21:120:35 | ...[...] | ActiveRecordInjection.rb:120:21:120:26 | call to params | ActiveRecordInjection.rb:120:21:120:35 | ...[...] | This SQL query depends on a $@. | ActiveRecordInjection.rb:120:21:120:26 | call to params | user-provided value | +| ActiveRecordInjection.rb:121:21:121:35 | ...[...] | ActiveRecordInjection.rb:121:21:121:26 | call to params | ActiveRecordInjection.rb:121:21:121:35 | ...[...] | This SQL query depends on a $@. | ActiveRecordInjection.rb:121:21:121:26 | call to params | user-provided value | +| ActiveRecordInjection.rb:122:20:122:34 | ...[...] | ActiveRecordInjection.rb:122:20:122:25 | call to params | ActiveRecordInjection.rb:122:20:122:34 | ...[...] | This SQL query depends on a $@. | ActiveRecordInjection.rb:122:20:122:25 | call to params | user-provided value | +| ActiveRecordInjection.rb:124:23:124:47 | ...[...] | ActiveRecordInjection.rb:124:23:124:28 | call to params | ActiveRecordInjection.rb:124:23:124:47 | ...[...] | This SQL query depends on a $@. | ActiveRecordInjection.rb:124:23:124:28 | call to params | user-provided value | +| ActiveRecordInjection.rb:128:19:128:30 | ...[...] | ActiveRecordInjection.rb:128:19:128:24 | call to params | ActiveRecordInjection.rb:128:19:128:30 | ...[...] | This SQL query depends on a $@. | ActiveRecordInjection.rb:128:19:128:24 | call to params | user-provided value | +| ActiveRecordInjection.rb:130:29:130:39 | ...[...] | ActiveRecordInjection.rb:130:29:130:34 | call to params | ActiveRecordInjection.rb:130:29:130:39 | ...[...] | This SQL query depends on a $@. | ActiveRecordInjection.rb:130:29:130:34 | call to params | user-provided value | +| ActiveRecordInjection.rb:142:20:142:32 | ... + ... | ActiveRecordInjection.rb:136:10:136:15 | call to params | ActiveRecordInjection.rb:142:20:142:32 | ... + ... | This SQL query depends on a $@. | ActiveRecordInjection.rb:136:10:136:15 | call to params | user-provided value | +| ActiveRecordInjection.rb:175:21:175:44 | ...[...] | ActiveRecordInjection.rb:175:21:175:26 | call to params | ActiveRecordInjection.rb:175:21:175:44 | ...[...] | This SQL query depends on a $@. | ActiveRecordInjection.rb:175:21:175:26 | call to params | user-provided value | +| ActiveRecordInjection.rb:189:27:189:76 | "this is an unsafe annotation:..." | ActiveRecordInjection.rb:189:59:189:64 | call to params | ActiveRecordInjection.rb:189:27:189:76 | "this is an unsafe annotation:..." | This SQL query depends on a $@. | ActiveRecordInjection.rb:189:59:189:64 | call to params | user-provided value | +| ActiveRecordInjection.rb:202:37:202:41 | query | ActiveRecordInjection.rb:207:5:207:10 | call to params | ActiveRecordInjection.rb:202:37:202:41 | query | This SQL query depends on a $@. | ActiveRecordInjection.rb:207:5:207:10 | call to params | user-provided value | +| ActiveRecordInjection.rb:211:43:211:104 | "SELECT * FROM users WHERE id ..." | ActiveRecordInjection.rb:207:5:207:10 | call to params | ActiveRecordInjection.rb:211:43:211:104 | "SELECT * FROM users WHERE id ..." | This SQL query depends on a $@. | ActiveRecordInjection.rb:207:5:207:10 | call to params | user-provided value | +| ActiveRecordInjection.rb:212:35:212:96 | "SELECT * FROM users WHERE id ..." | ActiveRecordInjection.rb:207:5:207:10 | call to params | ActiveRecordInjection.rb:212:35:212:96 | "SELECT * FROM users WHERE id ..." | This SQL query depends on a $@. | ActiveRecordInjection.rb:207:5:207:10 | call to params | user-provided value | +| ActiveRecordInjection.rb:217:38:217:53 | "role = #{...}" | ActiveRecordInjection.rb:223:29:223:34 | call to params | ActiveRecordInjection.rb:217:38:217:53 | "role = #{...}" | This SQL query depends on a $@. | ActiveRecordInjection.rb:223:29:223:34 | call to params | user-provided value | +| ArelInjection.rb:6:20:6:61 | "SELECT * FROM users WHERE nam..." | ArelInjection.rb:4:12:4:17 | call to params | ArelInjection.rb:6:20:6:61 | "SELECT * FROM users WHERE nam..." | This SQL query depends on a $@. | ArelInjection.rb:4:12:4:17 | call to params | user-provided value | +| ArelInjection.rb:7:39:7:80 | "SELECT * FROM users WHERE nam..." | ArelInjection.rb:4:12:4:17 | call to params | ArelInjection.rb:7:39:7:80 | "SELECT * FROM users WHERE nam..." | This SQL query depends on a $@. | ArelInjection.rb:4:12:4:17 | call to params | user-provided value | +| PgInjection.rb:14:15:14:18 | qry1 | PgInjection.rb:6:12:6:17 | call to params | PgInjection.rb:14:15:14:18 | qry1 | This SQL query depends on a $@. | PgInjection.rb:6:12:6:17 | call to params | user-provided value | +| PgInjection.rb:15:21:15:24 | qry1 | PgInjection.rb:6:12:6:17 | call to params | PgInjection.rb:15:21:15:24 | qry1 | This SQL query depends on a $@. | PgInjection.rb:6:12:6:17 | call to params | user-provided value | +| PgInjection.rb:20:22:20:25 | qry2 | PgInjection.rb:6:12:6:17 | call to params | PgInjection.rb:20:22:20:25 | qry2 | This SQL query depends on a $@. | PgInjection.rb:6:12:6:17 | call to params | user-provided value | +| PgInjection.rb:21:28:21:31 | qry2 | PgInjection.rb:6:12:6:17 | call to params | PgInjection.rb:21:28:21:31 | qry2 | This SQL query depends on a $@. | PgInjection.rb:6:12:6:17 | call to params | user-provided value | +| PgInjection.rb:32:29:32:32 | qry3 | PgInjection.rb:6:12:6:17 | call to params | PgInjection.rb:32:29:32:32 | qry3 | This SQL query depends on a $@. | PgInjection.rb:6:12:6:17 | call to params | user-provided value | +| PgInjection.rb:44:29:44:32 | qry3 | PgInjection.rb:6:12:6:17 | call to params | PgInjection.rb:44:29:44:32 | qry3 | This SQL query depends on a $@. | PgInjection.rb:6:12:6:17 | call to params | user-provided value | edges | ActiveRecordInjection.rb:8:25:8:28 | name | ActiveRecordInjection.rb:10:33:10:67 | "name='#{...}' and pass='#{...}'" | provenance | AdditionalTaintStep | | ActiveRecordInjection.rb:8:25:8:28 | name | ActiveRecordInjection.rb:12:31:12:65 | "name='#{...}' and pass='#{...}'" | provenance | AdditionalTaintStep | @@ -19,64 +68,64 @@ edges | ActiveRecordInjection.rb:64:32:64:42 | ...[...] | ActiveRecordInjection.rb:64:23:64:45 | "id = '#{...}'" : String | provenance | AdditionalTaintStep | | ActiveRecordInjection.rb:69:21:69:26 | call to params | ActiveRecordInjection.rb:69:21:69:35 | ...[...] | provenance | | | ActiveRecordInjection.rb:69:21:69:35 | ...[...] | ActiveRecordInjection.rb:68:16:68:21 | <<-SQL | provenance | AdditionalTaintStep | -| ActiveRecordInjection.rb:75:34:75:39 | call to params | ActiveRecordInjection.rb:75:34:75:44 | ...[...] | provenance | | -| ActiveRecordInjection.rb:75:34:75:44 | ...[...] | ActiveRecordInjection.rb:75:20:75:47 | "user.id = '#{...}'" | provenance | AdditionalTaintStep | -| ActiveRecordInjection.rb:77:23:77:28 | call to params | ActiveRecordInjection.rb:77:23:77:35 | ...[...] | provenance | | -| ActiveRecordInjection.rb:77:23:77:35 | ...[...] | ActiveRecordInjection.rb:8:25:8:28 | name | provenance | | -| ActiveRecordInjection.rb:77:38:77:43 | call to params | ActiveRecordInjection.rb:77:38:77:50 | ...[...] | provenance | | -| ActiveRecordInjection.rb:77:38:77:50 | ...[...] | ActiveRecordInjection.rb:8:31:8:34 | pass | provenance | | -| ActiveRecordInjection.rb:81:41:81:46 | call to params | ActiveRecordInjection.rb:81:41:81:51 | ...[...] | provenance | | -| ActiveRecordInjection.rb:81:41:81:51 | ...[...] | ActiveRecordInjection.rb:81:32:81:54 | "id = '#{...}'" | provenance | AdditionalTaintStep | -| ActiveRecordInjection.rb:86:23:86:28 | call to params | ActiveRecordInjection.rb:86:23:86:35 | ...[...] | provenance | | -| ActiveRecordInjection.rb:90:17:90:22 | call to params | ActiveRecordInjection.rb:90:17:90:31 | ...[...] | provenance | | -| ActiveRecordInjection.rb:91:19:91:24 | call to params | ActiveRecordInjection.rb:91:19:91:33 | ...[...] | provenance | | -| ActiveRecordInjection.rb:95:18:95:23 | call to params | ActiveRecordInjection.rb:95:18:95:35 | ...[...] | provenance | | -| ActiveRecordInjection.rb:99:21:99:26 | call to params | ActiveRecordInjection.rb:99:21:99:35 | ...[...] | provenance | | -| ActiveRecordInjection.rb:107:31:107:36 | call to params | ActiveRecordInjection.rb:107:31:107:52 | ...[...] | provenance | | -| ActiveRecordInjection.rb:107:31:107:52 | ...[...] | ActiveRecordInjection.rb:107:20:107:55 | "name = '#{...}'" | provenance | AdditionalTaintStep | -| ActiveRecordInjection.rb:111:30:111:35 | call to params | ActiveRecordInjection.rb:111:30:111:51 | ...[...] | provenance | | -| ActiveRecordInjection.rb:111:30:111:51 | ...[...] | ActiveRecordInjection.rb:111:19:111:54 | "name = '#{...}'" | provenance | AdditionalTaintStep | -| ActiveRecordInjection.rb:113:18:113:23 | call to params | ActiveRecordInjection.rb:113:18:113:35 | ...[...] | provenance | | -| ActiveRecordInjection.rb:115:26:115:31 | call to params | ActiveRecordInjection.rb:115:26:115:40 | ...[...] | provenance | | -| ActiveRecordInjection.rb:116:28:116:33 | call to params | ActiveRecordInjection.rb:116:28:116:42 | ...[...] | provenance | | -| ActiveRecordInjection.rb:117:30:117:35 | call to params | ActiveRecordInjection.rb:117:30:117:47 | ...[...] | provenance | | -| ActiveRecordInjection.rb:117:30:117:47 | ...[...] | ActiveRecordInjection.rb:117:25:117:49 | "b #{...}" | provenance | AdditionalTaintStep | -| ActiveRecordInjection.rb:118:32:118:37 | call to params | ActiveRecordInjection.rb:118:32:118:49 | ...[...] | provenance | | -| ActiveRecordInjection.rb:118:32:118:49 | ...[...] | ActiveRecordInjection.rb:118:27:118:51 | "b #{...}" | provenance | AdditionalTaintStep | -| ActiveRecordInjection.rb:119:21:119:26 | call to params | ActiveRecordInjection.rb:119:21:119:35 | ...[...] | provenance | | +| ActiveRecordInjection.rb:76:34:76:39 | call to params | ActiveRecordInjection.rb:76:34:76:44 | ...[...] | provenance | | +| ActiveRecordInjection.rb:76:34:76:44 | ...[...] | ActiveRecordInjection.rb:76:20:76:47 | "user.id = '#{...}'" | provenance | AdditionalTaintStep | +| ActiveRecordInjection.rb:78:23:78:28 | call to params | ActiveRecordInjection.rb:78:23:78:35 | ...[...] | provenance | | +| ActiveRecordInjection.rb:78:23:78:35 | ...[...] | ActiveRecordInjection.rb:8:25:8:28 | name | provenance | | +| ActiveRecordInjection.rb:78:38:78:43 | call to params | ActiveRecordInjection.rb:78:38:78:50 | ...[...] | provenance | | +| ActiveRecordInjection.rb:78:38:78:50 | ...[...] | ActiveRecordInjection.rb:8:31:8:34 | pass | provenance | | +| ActiveRecordInjection.rb:82:41:82:46 | call to params | ActiveRecordInjection.rb:82:41:82:51 | ...[...] | provenance | | +| ActiveRecordInjection.rb:82:41:82:51 | ...[...] | ActiveRecordInjection.rb:82:32:82:54 | "id = '#{...}'" | provenance | AdditionalTaintStep | +| ActiveRecordInjection.rb:87:23:87:28 | call to params | ActiveRecordInjection.rb:87:23:87:35 | ...[...] | provenance | | +| ActiveRecordInjection.rb:91:17:91:22 | call to params | ActiveRecordInjection.rb:91:17:91:31 | ...[...] | provenance | | +| ActiveRecordInjection.rb:92:19:92:24 | call to params | ActiveRecordInjection.rb:92:19:92:33 | ...[...] | provenance | | +| ActiveRecordInjection.rb:96:18:96:23 | call to params | ActiveRecordInjection.rb:96:18:96:35 | ...[...] | provenance | | +| ActiveRecordInjection.rb:100:21:100:26 | call to params | ActiveRecordInjection.rb:100:21:100:35 | ...[...] | provenance | | +| ActiveRecordInjection.rb:108:31:108:36 | call to params | ActiveRecordInjection.rb:108:31:108:52 | ...[...] | provenance | | +| ActiveRecordInjection.rb:108:31:108:52 | ...[...] | ActiveRecordInjection.rb:108:20:108:55 | "name = '#{...}'" | provenance | AdditionalTaintStep | +| ActiveRecordInjection.rb:112:30:112:35 | call to params | ActiveRecordInjection.rb:112:30:112:51 | ...[...] | provenance | | +| ActiveRecordInjection.rb:112:30:112:51 | ...[...] | ActiveRecordInjection.rb:112:19:112:54 | "name = '#{...}'" | provenance | AdditionalTaintStep | +| ActiveRecordInjection.rb:114:18:114:23 | call to params | ActiveRecordInjection.rb:114:18:114:35 | ...[...] | provenance | | +| ActiveRecordInjection.rb:116:26:116:31 | call to params | ActiveRecordInjection.rb:116:26:116:40 | ...[...] | provenance | | +| ActiveRecordInjection.rb:117:28:117:33 | call to params | ActiveRecordInjection.rb:117:28:117:42 | ...[...] | provenance | | +| ActiveRecordInjection.rb:118:30:118:35 | call to params | ActiveRecordInjection.rb:118:30:118:47 | ...[...] | provenance | | +| ActiveRecordInjection.rb:118:30:118:47 | ...[...] | ActiveRecordInjection.rb:118:25:118:49 | "b #{...}" | provenance | AdditionalTaintStep | +| ActiveRecordInjection.rb:119:32:119:37 | call to params | ActiveRecordInjection.rb:119:32:119:49 | ...[...] | provenance | | +| ActiveRecordInjection.rb:119:32:119:49 | ...[...] | ActiveRecordInjection.rb:119:27:119:51 | "b #{...}" | provenance | AdditionalTaintStep | | ActiveRecordInjection.rb:120:21:120:26 | call to params | ActiveRecordInjection.rb:120:21:120:35 | ...[...] | provenance | | -| ActiveRecordInjection.rb:121:20:121:25 | call to params | ActiveRecordInjection.rb:121:20:121:34 | ...[...] | provenance | | -| ActiveRecordInjection.rb:123:23:123:28 | call to params | ActiveRecordInjection.rb:123:23:123:47 | ...[...] | provenance | | -| ActiveRecordInjection.rb:127:19:127:24 | call to params | ActiveRecordInjection.rb:127:19:127:30 | ...[...] | provenance | | -| ActiveRecordInjection.rb:129:29:129:34 | call to params | ActiveRecordInjection.rb:129:29:129:39 | ...[...] | provenance | | -| ActiveRecordInjection.rb:135:5:135:6 | ps | ActiveRecordInjection.rb:136:11:136:12 | ps | provenance | | -| ActiveRecordInjection.rb:135:10:135:15 | call to params | ActiveRecordInjection.rb:135:5:135:6 | ps | provenance | | -| ActiveRecordInjection.rb:136:5:136:7 | uid | ActiveRecordInjection.rb:137:5:137:9 | uidEq : String | provenance | AdditionalTaintStep | -| ActiveRecordInjection.rb:136:11:136:12 | ps | ActiveRecordInjection.rb:136:11:136:17 | ...[...] | provenance | | -| ActiveRecordInjection.rb:136:11:136:17 | ...[...] | ActiveRecordInjection.rb:136:5:136:7 | uid | provenance | | -| ActiveRecordInjection.rb:137:5:137:9 | uidEq : String | ActiveRecordInjection.rb:141:20:141:32 | ... + ... | provenance | | -| ActiveRecordInjection.rb:174:21:174:26 | call to params | ActiveRecordInjection.rb:174:21:174:44 | ...[...] | provenance | | -| ActiveRecordInjection.rb:174:21:174:26 | call to params | ActiveRecordInjection.rb:174:21:174:44 | ...[...] | provenance | | -| ActiveRecordInjection.rb:174:21:174:44 | ...[...] | ActiveRecordInjection.rb:27:22:27:30 | condition | provenance | | -| ActiveRecordInjection.rb:188:59:188:64 | call to params | ActiveRecordInjection.rb:188:59:188:74 | ...[...] | provenance | | -| ActiveRecordInjection.rb:188:59:188:74 | ...[...] | ActiveRecordInjection.rb:188:27:188:76 | "this is an unsafe annotation:..." | provenance | AdditionalTaintStep | -| ActiveRecordInjection.rb:199:5:199:13 | my_params | ActiveRecordInjection.rb:200:47:200:55 | my_params | provenance | | -| ActiveRecordInjection.rb:199:17:199:32 | call to permitted_params | ActiveRecordInjection.rb:199:5:199:13 | my_params | provenance | | -| ActiveRecordInjection.rb:200:5:200:9 | query : String | ActiveRecordInjection.rb:201:37:201:41 | query | provenance | | -| ActiveRecordInjection.rb:200:47:200:55 | my_params | ActiveRecordInjection.rb:200:47:200:65 | ...[...] | provenance | | -| ActiveRecordInjection.rb:200:47:200:65 | ...[...] | ActiveRecordInjection.rb:200:5:200:9 | query : String | provenance | AdditionalTaintStep | -| ActiveRecordInjection.rb:206:5:206:10 | call to params | ActiveRecordInjection.rb:206:5:206:27 | call to require | provenance | | -| ActiveRecordInjection.rb:206:5:206:27 | call to require | ActiveRecordInjection.rb:206:5:206:59 | call to permit | provenance | | -| ActiveRecordInjection.rb:206:5:206:59 | call to permit | ActiveRecordInjection.rb:199:17:199:32 | call to permitted_params | provenance | | -| ActiveRecordInjection.rb:206:5:206:59 | call to permit | ActiveRecordInjection.rb:210:77:210:92 | call to permitted_params | provenance | | -| ActiveRecordInjection.rb:206:5:206:59 | call to permit | ActiveRecordInjection.rb:211:69:211:84 | call to permitted_params | provenance | | -| ActiveRecordInjection.rb:210:77:210:92 | call to permitted_params | ActiveRecordInjection.rb:210:77:210:102 | ...[...] | provenance | | -| ActiveRecordInjection.rb:210:77:210:102 | ...[...] | ActiveRecordInjection.rb:210:43:210:104 | "SELECT * FROM users WHERE id ..." | provenance | AdditionalTaintStep | -| ActiveRecordInjection.rb:211:69:211:84 | call to permitted_params | ActiveRecordInjection.rb:211:69:211:94 | ...[...] | provenance | | -| ActiveRecordInjection.rb:211:69:211:94 | ...[...] | ActiveRecordInjection.rb:211:35:211:96 | "SELECT * FROM users WHERE id ..." | provenance | AdditionalTaintStep | -| ActiveRecordInjection.rb:216:24:216:27 | role | ActiveRecordInjection.rb:216:38:216:53 | "role = #{...}" | provenance | AdditionalTaintStep | -| ActiveRecordInjection.rb:222:29:222:34 | call to params | ActiveRecordInjection.rb:222:29:222:41 | ...[...] | provenance | | -| ActiveRecordInjection.rb:222:29:222:41 | ...[...] | ActiveRecordInjection.rb:216:24:216:27 | role | provenance | | +| ActiveRecordInjection.rb:121:21:121:26 | call to params | ActiveRecordInjection.rb:121:21:121:35 | ...[...] | provenance | | +| ActiveRecordInjection.rb:122:20:122:25 | call to params | ActiveRecordInjection.rb:122:20:122:34 | ...[...] | provenance | | +| ActiveRecordInjection.rb:124:23:124:28 | call to params | ActiveRecordInjection.rb:124:23:124:47 | ...[...] | provenance | | +| ActiveRecordInjection.rb:128:19:128:24 | call to params | ActiveRecordInjection.rb:128:19:128:30 | ...[...] | provenance | | +| ActiveRecordInjection.rb:130:29:130:34 | call to params | ActiveRecordInjection.rb:130:29:130:39 | ...[...] | provenance | | +| ActiveRecordInjection.rb:136:5:136:6 | ps | ActiveRecordInjection.rb:137:11:137:12 | ps | provenance | | +| ActiveRecordInjection.rb:136:10:136:15 | call to params | ActiveRecordInjection.rb:136:5:136:6 | ps | provenance | | +| ActiveRecordInjection.rb:137:5:137:7 | uid | ActiveRecordInjection.rb:138:5:138:9 | uidEq : String | provenance | AdditionalTaintStep | +| ActiveRecordInjection.rb:137:11:137:12 | ps | ActiveRecordInjection.rb:137:11:137:17 | ...[...] | provenance | | +| ActiveRecordInjection.rb:137:11:137:17 | ...[...] | ActiveRecordInjection.rb:137:5:137:7 | uid | provenance | | +| ActiveRecordInjection.rb:138:5:138:9 | uidEq : String | ActiveRecordInjection.rb:142:20:142:32 | ... + ... | provenance | | +| ActiveRecordInjection.rb:175:21:175:26 | call to params | ActiveRecordInjection.rb:175:21:175:44 | ...[...] | provenance | | +| ActiveRecordInjection.rb:175:21:175:26 | call to params | ActiveRecordInjection.rb:175:21:175:44 | ...[...] | provenance | | +| ActiveRecordInjection.rb:175:21:175:44 | ...[...] | ActiveRecordInjection.rb:27:22:27:30 | condition | provenance | | +| ActiveRecordInjection.rb:189:59:189:64 | call to params | ActiveRecordInjection.rb:189:59:189:74 | ...[...] | provenance | | +| ActiveRecordInjection.rb:189:59:189:74 | ...[...] | ActiveRecordInjection.rb:189:27:189:76 | "this is an unsafe annotation:..." | provenance | AdditionalTaintStep | +| ActiveRecordInjection.rb:200:5:200:13 | my_params | ActiveRecordInjection.rb:201:47:201:55 | my_params | provenance | | +| ActiveRecordInjection.rb:200:17:200:32 | call to permitted_params | ActiveRecordInjection.rb:200:5:200:13 | my_params | provenance | | +| ActiveRecordInjection.rb:201:5:201:9 | query : String | ActiveRecordInjection.rb:202:37:202:41 | query | provenance | | +| ActiveRecordInjection.rb:201:47:201:55 | my_params | ActiveRecordInjection.rb:201:47:201:65 | ...[...] | provenance | | +| ActiveRecordInjection.rb:201:47:201:65 | ...[...] | ActiveRecordInjection.rb:201:5:201:9 | query : String | provenance | AdditionalTaintStep | +| ActiveRecordInjection.rb:207:5:207:10 | call to params | ActiveRecordInjection.rb:207:5:207:27 | call to require | provenance | | +| ActiveRecordInjection.rb:207:5:207:27 | call to require | ActiveRecordInjection.rb:207:5:207:59 | call to permit | provenance | | +| ActiveRecordInjection.rb:207:5:207:59 | call to permit | ActiveRecordInjection.rb:200:17:200:32 | call to permitted_params | provenance | | +| ActiveRecordInjection.rb:207:5:207:59 | call to permit | ActiveRecordInjection.rb:211:77:211:92 | call to permitted_params | provenance | | +| ActiveRecordInjection.rb:207:5:207:59 | call to permit | ActiveRecordInjection.rb:212:69:212:84 | call to permitted_params | provenance | | +| ActiveRecordInjection.rb:211:77:211:92 | call to permitted_params | ActiveRecordInjection.rb:211:77:211:102 | ...[...] | provenance | | +| ActiveRecordInjection.rb:211:77:211:102 | ...[...] | ActiveRecordInjection.rb:211:43:211:104 | "SELECT * FROM users WHERE id ..." | provenance | AdditionalTaintStep | +| ActiveRecordInjection.rb:212:69:212:84 | call to permitted_params | ActiveRecordInjection.rb:212:69:212:94 | ...[...] | provenance | | +| ActiveRecordInjection.rb:212:69:212:94 | ...[...] | ActiveRecordInjection.rb:212:35:212:96 | "SELECT * FROM users WHERE id ..." | provenance | AdditionalTaintStep | +| ActiveRecordInjection.rb:217:24:217:27 | role | ActiveRecordInjection.rb:217:38:217:53 | "role = #{...}" | provenance | AdditionalTaintStep | +| ActiveRecordInjection.rb:223:29:223:34 | call to params | ActiveRecordInjection.rb:223:29:223:41 | ...[...] | provenance | | +| ActiveRecordInjection.rb:223:29:223:41 | ...[...] | ActiveRecordInjection.rb:217:24:217:27 | role | provenance | | | ArelInjection.rb:4:5:4:8 | name | ArelInjection.rb:6:20:6:61 | "SELECT * FROM users WHERE nam..." | provenance | AdditionalTaintStep | | ArelInjection.rb:4:5:4:8 | name | ArelInjection.rb:7:39:7:80 | "SELECT * FROM users WHERE nam..." | provenance | AdditionalTaintStep | | ArelInjection.rb:4:12:4:17 | call to params | ArelInjection.rb:4:12:4:29 | ...[...] | provenance | | @@ -122,88 +171,88 @@ nodes | ActiveRecordInjection.rb:68:16:68:21 | <<-SQL | semmle.label | <<-SQL | | ActiveRecordInjection.rb:69:21:69:26 | call to params | semmle.label | call to params | | ActiveRecordInjection.rb:69:21:69:35 | ...[...] | semmle.label | ...[...] | -| ActiveRecordInjection.rb:75:20:75:47 | "user.id = '#{...}'" | semmle.label | "user.id = '#{...}'" | -| ActiveRecordInjection.rb:75:34:75:39 | call to params | semmle.label | call to params | -| ActiveRecordInjection.rb:75:34:75:44 | ...[...] | semmle.label | ...[...] | -| ActiveRecordInjection.rb:77:23:77:28 | call to params | semmle.label | call to params | -| ActiveRecordInjection.rb:77:23:77:35 | ...[...] | semmle.label | ...[...] | -| ActiveRecordInjection.rb:77:38:77:43 | call to params | semmle.label | call to params | -| ActiveRecordInjection.rb:77:38:77:50 | ...[...] | semmle.label | ...[...] | -| ActiveRecordInjection.rb:81:32:81:54 | "id = '#{...}'" | semmle.label | "id = '#{...}'" | -| ActiveRecordInjection.rb:81:41:81:46 | call to params | semmle.label | call to params | -| ActiveRecordInjection.rb:81:41:81:51 | ...[...] | semmle.label | ...[...] | -| ActiveRecordInjection.rb:86:23:86:28 | call to params | semmle.label | call to params | -| ActiveRecordInjection.rb:86:23:86:35 | ...[...] | semmle.label | ...[...] | -| ActiveRecordInjection.rb:90:17:90:22 | call to params | semmle.label | call to params | -| ActiveRecordInjection.rb:90:17:90:31 | ...[...] | semmle.label | ...[...] | -| ActiveRecordInjection.rb:91:19:91:24 | call to params | semmle.label | call to params | -| ActiveRecordInjection.rb:91:19:91:33 | ...[...] | semmle.label | ...[...] | -| ActiveRecordInjection.rb:95:18:95:23 | call to params | semmle.label | call to params | -| ActiveRecordInjection.rb:95:18:95:35 | ...[...] | semmle.label | ...[...] | -| ActiveRecordInjection.rb:99:21:99:26 | call to params | semmle.label | call to params | -| ActiveRecordInjection.rb:99:21:99:35 | ...[...] | semmle.label | ...[...] | -| ActiveRecordInjection.rb:107:20:107:55 | "name = '#{...}'" | semmle.label | "name = '#{...}'" | -| ActiveRecordInjection.rb:107:31:107:36 | call to params | semmle.label | call to params | -| ActiveRecordInjection.rb:107:31:107:52 | ...[...] | semmle.label | ...[...] | -| ActiveRecordInjection.rb:111:19:111:54 | "name = '#{...}'" | semmle.label | "name = '#{...}'" | -| ActiveRecordInjection.rb:111:30:111:35 | call to params | semmle.label | call to params | -| ActiveRecordInjection.rb:111:30:111:51 | ...[...] | semmle.label | ...[...] | -| ActiveRecordInjection.rb:113:18:113:23 | call to params | semmle.label | call to params | -| ActiveRecordInjection.rb:113:18:113:35 | ...[...] | semmle.label | ...[...] | -| ActiveRecordInjection.rb:115:26:115:31 | call to params | semmle.label | call to params | -| ActiveRecordInjection.rb:115:26:115:40 | ...[...] | semmle.label | ...[...] | -| ActiveRecordInjection.rb:116:28:116:33 | call to params | semmle.label | call to params | -| ActiveRecordInjection.rb:116:28:116:42 | ...[...] | semmle.label | ...[...] | -| ActiveRecordInjection.rb:117:25:117:49 | "b #{...}" | semmle.label | "b #{...}" | -| ActiveRecordInjection.rb:117:30:117:35 | call to params | semmle.label | call to params | -| ActiveRecordInjection.rb:117:30:117:47 | ...[...] | semmle.label | ...[...] | -| ActiveRecordInjection.rb:118:27:118:51 | "b #{...}" | semmle.label | "b #{...}" | -| ActiveRecordInjection.rb:118:32:118:37 | call to params | semmle.label | call to params | -| ActiveRecordInjection.rb:118:32:118:49 | ...[...] | semmle.label | ...[...] | -| ActiveRecordInjection.rb:119:21:119:26 | call to params | semmle.label | call to params | -| ActiveRecordInjection.rb:119:21:119:35 | ...[...] | semmle.label | ...[...] | +| ActiveRecordInjection.rb:76:20:76:47 | "user.id = '#{...}'" | semmle.label | "user.id = '#{...}'" | +| ActiveRecordInjection.rb:76:34:76:39 | call to params | semmle.label | call to params | +| ActiveRecordInjection.rb:76:34:76:44 | ...[...] | semmle.label | ...[...] | +| ActiveRecordInjection.rb:78:23:78:28 | call to params | semmle.label | call to params | +| ActiveRecordInjection.rb:78:23:78:35 | ...[...] | semmle.label | ...[...] | +| ActiveRecordInjection.rb:78:38:78:43 | call to params | semmle.label | call to params | +| ActiveRecordInjection.rb:78:38:78:50 | ...[...] | semmle.label | ...[...] | +| ActiveRecordInjection.rb:82:32:82:54 | "id = '#{...}'" | semmle.label | "id = '#{...}'" | +| ActiveRecordInjection.rb:82:41:82:46 | call to params | semmle.label | call to params | +| ActiveRecordInjection.rb:82:41:82:51 | ...[...] | semmle.label | ...[...] | +| ActiveRecordInjection.rb:87:23:87:28 | call to params | semmle.label | call to params | +| ActiveRecordInjection.rb:87:23:87:35 | ...[...] | semmle.label | ...[...] | +| ActiveRecordInjection.rb:91:17:91:22 | call to params | semmle.label | call to params | +| ActiveRecordInjection.rb:91:17:91:31 | ...[...] | semmle.label | ...[...] | +| ActiveRecordInjection.rb:92:19:92:24 | call to params | semmle.label | call to params | +| ActiveRecordInjection.rb:92:19:92:33 | ...[...] | semmle.label | ...[...] | +| ActiveRecordInjection.rb:96:18:96:23 | call to params | semmle.label | call to params | +| ActiveRecordInjection.rb:96:18:96:35 | ...[...] | semmle.label | ...[...] | +| ActiveRecordInjection.rb:100:21:100:26 | call to params | semmle.label | call to params | +| ActiveRecordInjection.rb:100:21:100:35 | ...[...] | semmle.label | ...[...] | +| ActiveRecordInjection.rb:108:20:108:55 | "name = '#{...}'" | semmle.label | "name = '#{...}'" | +| ActiveRecordInjection.rb:108:31:108:36 | call to params | semmle.label | call to params | +| ActiveRecordInjection.rb:108:31:108:52 | ...[...] | semmle.label | ...[...] | +| ActiveRecordInjection.rb:112:19:112:54 | "name = '#{...}'" | semmle.label | "name = '#{...}'" | +| ActiveRecordInjection.rb:112:30:112:35 | call to params | semmle.label | call to params | +| ActiveRecordInjection.rb:112:30:112:51 | ...[...] | semmle.label | ...[...] | +| ActiveRecordInjection.rb:114:18:114:23 | call to params | semmle.label | call to params | +| ActiveRecordInjection.rb:114:18:114:35 | ...[...] | semmle.label | ...[...] | +| ActiveRecordInjection.rb:116:26:116:31 | call to params | semmle.label | call to params | +| ActiveRecordInjection.rb:116:26:116:40 | ...[...] | semmle.label | ...[...] | +| ActiveRecordInjection.rb:117:28:117:33 | call to params | semmle.label | call to params | +| ActiveRecordInjection.rb:117:28:117:42 | ...[...] | semmle.label | ...[...] | +| ActiveRecordInjection.rb:118:25:118:49 | "b #{...}" | semmle.label | "b #{...}" | +| ActiveRecordInjection.rb:118:30:118:35 | call to params | semmle.label | call to params | +| ActiveRecordInjection.rb:118:30:118:47 | ...[...] | semmle.label | ...[...] | +| ActiveRecordInjection.rb:119:27:119:51 | "b #{...}" | semmle.label | "b #{...}" | +| ActiveRecordInjection.rb:119:32:119:37 | call to params | semmle.label | call to params | +| ActiveRecordInjection.rb:119:32:119:49 | ...[...] | semmle.label | ...[...] | | ActiveRecordInjection.rb:120:21:120:26 | call to params | semmle.label | call to params | | ActiveRecordInjection.rb:120:21:120:35 | ...[...] | semmle.label | ...[...] | -| ActiveRecordInjection.rb:121:20:121:25 | call to params | semmle.label | call to params | -| ActiveRecordInjection.rb:121:20:121:34 | ...[...] | semmle.label | ...[...] | -| ActiveRecordInjection.rb:123:23:123:28 | call to params | semmle.label | call to params | -| ActiveRecordInjection.rb:123:23:123:47 | ...[...] | semmle.label | ...[...] | -| ActiveRecordInjection.rb:127:19:127:24 | call to params | semmle.label | call to params | -| ActiveRecordInjection.rb:127:19:127:30 | ...[...] | semmle.label | ...[...] | -| ActiveRecordInjection.rb:129:29:129:34 | call to params | semmle.label | call to params | -| ActiveRecordInjection.rb:129:29:129:39 | ...[...] | semmle.label | ...[...] | -| ActiveRecordInjection.rb:135:5:135:6 | ps | semmle.label | ps | -| ActiveRecordInjection.rb:135:10:135:15 | call to params | semmle.label | call to params | -| ActiveRecordInjection.rb:136:5:136:7 | uid | semmle.label | uid | -| ActiveRecordInjection.rb:136:11:136:12 | ps | semmle.label | ps | -| ActiveRecordInjection.rb:136:11:136:17 | ...[...] | semmle.label | ...[...] | -| ActiveRecordInjection.rb:137:5:137:9 | uidEq : String | semmle.label | uidEq : String | -| ActiveRecordInjection.rb:141:20:141:32 | ... + ... | semmle.label | ... + ... | -| ActiveRecordInjection.rb:174:21:174:26 | call to params | semmle.label | call to params | -| ActiveRecordInjection.rb:174:21:174:44 | ...[...] | semmle.label | ...[...] | -| ActiveRecordInjection.rb:174:21:174:44 | ...[...] | semmle.label | ...[...] | -| ActiveRecordInjection.rb:188:27:188:76 | "this is an unsafe annotation:..." | semmle.label | "this is an unsafe annotation:..." | -| ActiveRecordInjection.rb:188:59:188:64 | call to params | semmle.label | call to params | -| ActiveRecordInjection.rb:188:59:188:74 | ...[...] | semmle.label | ...[...] | -| ActiveRecordInjection.rb:199:5:199:13 | my_params | semmle.label | my_params | -| ActiveRecordInjection.rb:199:17:199:32 | call to permitted_params | semmle.label | call to permitted_params | -| ActiveRecordInjection.rb:200:5:200:9 | query : String | semmle.label | query : String | -| ActiveRecordInjection.rb:200:47:200:55 | my_params | semmle.label | my_params | -| ActiveRecordInjection.rb:200:47:200:65 | ...[...] | semmle.label | ...[...] | -| ActiveRecordInjection.rb:201:37:201:41 | query | semmle.label | query | -| ActiveRecordInjection.rb:206:5:206:10 | call to params | semmle.label | call to params | -| ActiveRecordInjection.rb:206:5:206:27 | call to require | semmle.label | call to require | -| ActiveRecordInjection.rb:206:5:206:59 | call to permit | semmle.label | call to permit | -| ActiveRecordInjection.rb:210:43:210:104 | "SELECT * FROM users WHERE id ..." | semmle.label | "SELECT * FROM users WHERE id ..." | -| ActiveRecordInjection.rb:210:77:210:92 | call to permitted_params | semmle.label | call to permitted_params | -| ActiveRecordInjection.rb:210:77:210:102 | ...[...] | semmle.label | ...[...] | -| ActiveRecordInjection.rb:211:35:211:96 | "SELECT * FROM users WHERE id ..." | semmle.label | "SELECT * FROM users WHERE id ..." | -| ActiveRecordInjection.rb:211:69:211:84 | call to permitted_params | semmle.label | call to permitted_params | -| ActiveRecordInjection.rb:211:69:211:94 | ...[...] | semmle.label | ...[...] | -| ActiveRecordInjection.rb:216:24:216:27 | role | semmle.label | role | -| ActiveRecordInjection.rb:216:38:216:53 | "role = #{...}" | semmle.label | "role = #{...}" | -| ActiveRecordInjection.rb:222:29:222:34 | call to params | semmle.label | call to params | -| ActiveRecordInjection.rb:222:29:222:41 | ...[...] | semmle.label | ...[...] | +| ActiveRecordInjection.rb:121:21:121:26 | call to params | semmle.label | call to params | +| ActiveRecordInjection.rb:121:21:121:35 | ...[...] | semmle.label | ...[...] | +| ActiveRecordInjection.rb:122:20:122:25 | call to params | semmle.label | call to params | +| ActiveRecordInjection.rb:122:20:122:34 | ...[...] | semmle.label | ...[...] | +| ActiveRecordInjection.rb:124:23:124:28 | call to params | semmle.label | call to params | +| ActiveRecordInjection.rb:124:23:124:47 | ...[...] | semmle.label | ...[...] | +| ActiveRecordInjection.rb:128:19:128:24 | call to params | semmle.label | call to params | +| ActiveRecordInjection.rb:128:19:128:30 | ...[...] | semmle.label | ...[...] | +| ActiveRecordInjection.rb:130:29:130:34 | call to params | semmle.label | call to params | +| ActiveRecordInjection.rb:130:29:130:39 | ...[...] | semmle.label | ...[...] | +| ActiveRecordInjection.rb:136:5:136:6 | ps | semmle.label | ps | +| ActiveRecordInjection.rb:136:10:136:15 | call to params | semmle.label | call to params | +| ActiveRecordInjection.rb:137:5:137:7 | uid | semmle.label | uid | +| ActiveRecordInjection.rb:137:11:137:12 | ps | semmle.label | ps | +| ActiveRecordInjection.rb:137:11:137:17 | ...[...] | semmle.label | ...[...] | +| ActiveRecordInjection.rb:138:5:138:9 | uidEq : String | semmle.label | uidEq : String | +| ActiveRecordInjection.rb:142:20:142:32 | ... + ... | semmle.label | ... + ... | +| ActiveRecordInjection.rb:175:21:175:26 | call to params | semmle.label | call to params | +| ActiveRecordInjection.rb:175:21:175:44 | ...[...] | semmle.label | ...[...] | +| ActiveRecordInjection.rb:175:21:175:44 | ...[...] | semmle.label | ...[...] | +| ActiveRecordInjection.rb:189:27:189:76 | "this is an unsafe annotation:..." | semmle.label | "this is an unsafe annotation:..." | +| ActiveRecordInjection.rb:189:59:189:64 | call to params | semmle.label | call to params | +| ActiveRecordInjection.rb:189:59:189:74 | ...[...] | semmle.label | ...[...] | +| ActiveRecordInjection.rb:200:5:200:13 | my_params | semmle.label | my_params | +| ActiveRecordInjection.rb:200:17:200:32 | call to permitted_params | semmle.label | call to permitted_params | +| ActiveRecordInjection.rb:201:5:201:9 | query : String | semmle.label | query : String | +| ActiveRecordInjection.rb:201:47:201:55 | my_params | semmle.label | my_params | +| ActiveRecordInjection.rb:201:47:201:65 | ...[...] | semmle.label | ...[...] | +| ActiveRecordInjection.rb:202:37:202:41 | query | semmle.label | query | +| ActiveRecordInjection.rb:207:5:207:10 | call to params | semmle.label | call to params | +| ActiveRecordInjection.rb:207:5:207:27 | call to require | semmle.label | call to require | +| ActiveRecordInjection.rb:207:5:207:59 | call to permit | semmle.label | call to permit | +| ActiveRecordInjection.rb:211:43:211:104 | "SELECT * FROM users WHERE id ..." | semmle.label | "SELECT * FROM users WHERE id ..." | +| ActiveRecordInjection.rb:211:77:211:92 | call to permitted_params | semmle.label | call to permitted_params | +| ActiveRecordInjection.rb:211:77:211:102 | ...[...] | semmle.label | ...[...] | +| ActiveRecordInjection.rb:212:35:212:96 | "SELECT * FROM users WHERE id ..." | semmle.label | "SELECT * FROM users WHERE id ..." | +| ActiveRecordInjection.rb:212:69:212:84 | call to permitted_params | semmle.label | call to permitted_params | +| ActiveRecordInjection.rb:212:69:212:94 | ...[...] | semmle.label | ...[...] | +| ActiveRecordInjection.rb:217:24:217:27 | role | semmle.label | role | +| ActiveRecordInjection.rb:217:38:217:53 | "role = #{...}" | semmle.label | "role = #{...}" | +| ActiveRecordInjection.rb:223:29:223:34 | call to params | semmle.label | call to params | +| ActiveRecordInjection.rb:223:29:223:41 | ...[...] | semmle.label | ...[...] | | ArelInjection.rb:4:5:4:8 | name | semmle.label | name | | ArelInjection.rb:4:12:4:17 | call to params | semmle.label | call to params | | ArelInjection.rb:4:12:4:29 | ...[...] | semmle.label | ...[...] | @@ -223,52 +272,3 @@ nodes | PgInjection.rb:43:5:43:8 | qry3 : String | semmle.label | qry3 : String | | PgInjection.rb:44:29:44:32 | qry3 | semmle.label | qry3 | subpaths -#select -| ActiveRecordInjection.rb:10:33:10:67 | "name='#{...}' and pass='#{...}'" | ActiveRecordInjection.rb:77:23:77:28 | call to params | ActiveRecordInjection.rb:10:33:10:67 | "name='#{...}' and pass='#{...}'" | This SQL query depends on a $@. | ActiveRecordInjection.rb:77:23:77:28 | call to params | user-provided value | -| ActiveRecordInjection.rb:10:33:10:67 | "name='#{...}' and pass='#{...}'" | ActiveRecordInjection.rb:77:38:77:43 | call to params | ActiveRecordInjection.rb:10:33:10:67 | "name='#{...}' and pass='#{...}'" | This SQL query depends on a $@. | ActiveRecordInjection.rb:77:38:77:43 | call to params | user-provided value | -| ActiveRecordInjection.rb:12:31:12:65 | "name='#{...}' and pass='#{...}'" | ActiveRecordInjection.rb:77:23:77:28 | call to params | ActiveRecordInjection.rb:12:31:12:65 | "name='#{...}' and pass='#{...}'" | This SQL query depends on a $@. | ActiveRecordInjection.rb:77:23:77:28 | call to params | user-provided value | -| ActiveRecordInjection.rb:12:31:12:65 | "name='#{...}' and pass='#{...}'" | ActiveRecordInjection.rb:77:38:77:43 | call to params | ActiveRecordInjection.rb:12:31:12:65 | "name='#{...}' and pass='#{...}'" | This SQL query depends on a $@. | ActiveRecordInjection.rb:77:38:77:43 | call to params | user-provided value | -| ActiveRecordInjection.rb:16:13:16:26 | "name=#{...}" | ActiveRecordInjection.rb:77:23:77:28 | call to params | ActiveRecordInjection.rb:16:13:16:26 | "name=#{...}" | This SQL query depends on a $@. | ActiveRecordInjection.rb:77:23:77:28 | call to params | user-provided value | -| ActiveRecordInjection.rb:30:16:30:24 | condition | ActiveRecordInjection.rb:174:21:174:26 | call to params | ActiveRecordInjection.rb:30:16:30:24 | condition | This SQL query depends on a $@. | ActiveRecordInjection.rb:174:21:174:26 | call to params | user-provided value | -| ActiveRecordInjection.rb:42:30:42:44 | ...[...] | ActiveRecordInjection.rb:42:30:42:35 | call to params | ActiveRecordInjection.rb:42:30:42:44 | ...[...] | This SQL query depends on a $@. | ActiveRecordInjection.rb:42:30:42:35 | call to params | user-provided value | -| ActiveRecordInjection.rb:46:18:46:32 | ...[...] | ActiveRecordInjection.rb:46:18:46:23 | call to params | ActiveRecordInjection.rb:46:18:46:32 | ...[...] | This SQL query depends on a $@. | ActiveRecordInjection.rb:46:18:46:23 | call to params | user-provided value | -| ActiveRecordInjection.rb:50:20:50:42 | "id = '#{...}'" | ActiveRecordInjection.rb:50:29:50:34 | call to params | ActiveRecordInjection.rb:50:20:50:42 | "id = '#{...}'" | This SQL query depends on a $@. | ActiveRecordInjection.rb:50:29:50:34 | call to params | user-provided value | -| ActiveRecordInjection.rb:55:21:55:43 | "id = '#{...}'" | ActiveRecordInjection.rb:55:30:55:35 | call to params | ActiveRecordInjection.rb:55:21:55:43 | "id = '#{...}'" | This SQL query depends on a $@. | ActiveRecordInjection.rb:55:30:55:35 | call to params | user-provided value | -| ActiveRecordInjection.rb:59:21:59:45 | call to [] | ActiveRecordInjection.rb:59:31:59:36 | call to params | ActiveRecordInjection.rb:59:21:59:45 | call to [] | This SQL query depends on a $@. | ActiveRecordInjection.rb:59:31:59:36 | call to params | user-provided value | -| ActiveRecordInjection.rb:64:22:64:46 | call to [] | ActiveRecordInjection.rb:64:32:64:37 | call to params | ActiveRecordInjection.rb:64:22:64:46 | call to [] | This SQL query depends on a $@. | ActiveRecordInjection.rb:64:32:64:37 | call to params | user-provided value | -| ActiveRecordInjection.rb:68:16:68:21 | <<-SQL | ActiveRecordInjection.rb:69:21:69:26 | call to params | ActiveRecordInjection.rb:68:16:68:21 | <<-SQL | This SQL query depends on a $@. | ActiveRecordInjection.rb:69:21:69:26 | call to params | user-provided value | -| ActiveRecordInjection.rb:75:20:75:47 | "user.id = '#{...}'" | ActiveRecordInjection.rb:75:34:75:39 | call to params | ActiveRecordInjection.rb:75:20:75:47 | "user.id = '#{...}'" | This SQL query depends on a $@. | ActiveRecordInjection.rb:75:34:75:39 | call to params | user-provided value | -| ActiveRecordInjection.rb:81:32:81:54 | "id = '#{...}'" | ActiveRecordInjection.rb:81:41:81:46 | call to params | ActiveRecordInjection.rb:81:32:81:54 | "id = '#{...}'" | This SQL query depends on a $@. | ActiveRecordInjection.rb:81:41:81:46 | call to params | user-provided value | -| ActiveRecordInjection.rb:86:23:86:35 | ...[...] | ActiveRecordInjection.rb:86:23:86:28 | call to params | ActiveRecordInjection.rb:86:23:86:35 | ...[...] | This SQL query depends on a $@. | ActiveRecordInjection.rb:86:23:86:28 | call to params | user-provided value | -| ActiveRecordInjection.rb:90:17:90:31 | ...[...] | ActiveRecordInjection.rb:90:17:90:22 | call to params | ActiveRecordInjection.rb:90:17:90:31 | ...[...] | This SQL query depends on a $@. | ActiveRecordInjection.rb:90:17:90:22 | call to params | user-provided value | -| ActiveRecordInjection.rb:91:19:91:33 | ...[...] | ActiveRecordInjection.rb:91:19:91:24 | call to params | ActiveRecordInjection.rb:91:19:91:33 | ...[...] | This SQL query depends on a $@. | ActiveRecordInjection.rb:91:19:91:24 | call to params | user-provided value | -| ActiveRecordInjection.rb:95:18:95:35 | ...[...] | ActiveRecordInjection.rb:95:18:95:23 | call to params | ActiveRecordInjection.rb:95:18:95:35 | ...[...] | This SQL query depends on a $@. | ActiveRecordInjection.rb:95:18:95:23 | call to params | user-provided value | -| ActiveRecordInjection.rb:99:21:99:35 | ...[...] | ActiveRecordInjection.rb:99:21:99:26 | call to params | ActiveRecordInjection.rb:99:21:99:35 | ...[...] | This SQL query depends on a $@. | ActiveRecordInjection.rb:99:21:99:26 | call to params | user-provided value | -| ActiveRecordInjection.rb:107:20:107:55 | "name = '#{...}'" | ActiveRecordInjection.rb:107:31:107:36 | call to params | ActiveRecordInjection.rb:107:20:107:55 | "name = '#{...}'" | This SQL query depends on a $@. | ActiveRecordInjection.rb:107:31:107:36 | call to params | user-provided value | -| ActiveRecordInjection.rb:111:19:111:54 | "name = '#{...}'" | ActiveRecordInjection.rb:111:30:111:35 | call to params | ActiveRecordInjection.rb:111:19:111:54 | "name = '#{...}'" | This SQL query depends on a $@. | ActiveRecordInjection.rb:111:30:111:35 | call to params | user-provided value | -| ActiveRecordInjection.rb:113:18:113:35 | ...[...] | ActiveRecordInjection.rb:113:18:113:23 | call to params | ActiveRecordInjection.rb:113:18:113:35 | ...[...] | This SQL query depends on a $@. | ActiveRecordInjection.rb:113:18:113:23 | call to params | user-provided value | -| ActiveRecordInjection.rb:115:26:115:40 | ...[...] | ActiveRecordInjection.rb:115:26:115:31 | call to params | ActiveRecordInjection.rb:115:26:115:40 | ...[...] | This SQL query depends on a $@. | ActiveRecordInjection.rb:115:26:115:31 | call to params | user-provided value | -| ActiveRecordInjection.rb:116:28:116:42 | ...[...] | ActiveRecordInjection.rb:116:28:116:33 | call to params | ActiveRecordInjection.rb:116:28:116:42 | ...[...] | This SQL query depends on a $@. | ActiveRecordInjection.rb:116:28:116:33 | call to params | user-provided value | -| ActiveRecordInjection.rb:117:25:117:49 | "b #{...}" | ActiveRecordInjection.rb:117:30:117:35 | call to params | ActiveRecordInjection.rb:117:25:117:49 | "b #{...}" | This SQL query depends on a $@. | ActiveRecordInjection.rb:117:30:117:35 | call to params | user-provided value | -| ActiveRecordInjection.rb:118:27:118:51 | "b #{...}" | ActiveRecordInjection.rb:118:32:118:37 | call to params | ActiveRecordInjection.rb:118:27:118:51 | "b #{...}" | This SQL query depends on a $@. | ActiveRecordInjection.rb:118:32:118:37 | call to params | user-provided value | -| ActiveRecordInjection.rb:119:21:119:35 | ...[...] | ActiveRecordInjection.rb:119:21:119:26 | call to params | ActiveRecordInjection.rb:119:21:119:35 | ...[...] | This SQL query depends on a $@. | ActiveRecordInjection.rb:119:21:119:26 | call to params | user-provided value | -| ActiveRecordInjection.rb:120:21:120:35 | ...[...] | ActiveRecordInjection.rb:120:21:120:26 | call to params | ActiveRecordInjection.rb:120:21:120:35 | ...[...] | This SQL query depends on a $@. | ActiveRecordInjection.rb:120:21:120:26 | call to params | user-provided value | -| ActiveRecordInjection.rb:121:20:121:34 | ...[...] | ActiveRecordInjection.rb:121:20:121:25 | call to params | ActiveRecordInjection.rb:121:20:121:34 | ...[...] | This SQL query depends on a $@. | ActiveRecordInjection.rb:121:20:121:25 | call to params | user-provided value | -| ActiveRecordInjection.rb:123:23:123:47 | ...[...] | ActiveRecordInjection.rb:123:23:123:28 | call to params | ActiveRecordInjection.rb:123:23:123:47 | ...[...] | This SQL query depends on a $@. | ActiveRecordInjection.rb:123:23:123:28 | call to params | user-provided value | -| ActiveRecordInjection.rb:127:19:127:30 | ...[...] | ActiveRecordInjection.rb:127:19:127:24 | call to params | ActiveRecordInjection.rb:127:19:127:30 | ...[...] | This SQL query depends on a $@. | ActiveRecordInjection.rb:127:19:127:24 | call to params | user-provided value | -| ActiveRecordInjection.rb:129:29:129:39 | ...[...] | ActiveRecordInjection.rb:129:29:129:34 | call to params | ActiveRecordInjection.rb:129:29:129:39 | ...[...] | This SQL query depends on a $@. | ActiveRecordInjection.rb:129:29:129:34 | call to params | user-provided value | -| ActiveRecordInjection.rb:141:20:141:32 | ... + ... | ActiveRecordInjection.rb:135:10:135:15 | call to params | ActiveRecordInjection.rb:141:20:141:32 | ... + ... | This SQL query depends on a $@. | ActiveRecordInjection.rb:135:10:135:15 | call to params | user-provided value | -| ActiveRecordInjection.rb:174:21:174:44 | ...[...] | ActiveRecordInjection.rb:174:21:174:26 | call to params | ActiveRecordInjection.rb:174:21:174:44 | ...[...] | This SQL query depends on a $@. | ActiveRecordInjection.rb:174:21:174:26 | call to params | user-provided value | -| ActiveRecordInjection.rb:188:27:188:76 | "this is an unsafe annotation:..." | ActiveRecordInjection.rb:188:59:188:64 | call to params | ActiveRecordInjection.rb:188:27:188:76 | "this is an unsafe annotation:..." | This SQL query depends on a $@. | ActiveRecordInjection.rb:188:59:188:64 | call to params | user-provided value | -| ActiveRecordInjection.rb:201:37:201:41 | query | ActiveRecordInjection.rb:206:5:206:10 | call to params | ActiveRecordInjection.rb:201:37:201:41 | query | This SQL query depends on a $@. | ActiveRecordInjection.rb:206:5:206:10 | call to params | user-provided value | -| ActiveRecordInjection.rb:210:43:210:104 | "SELECT * FROM users WHERE id ..." | ActiveRecordInjection.rb:206:5:206:10 | call to params | ActiveRecordInjection.rb:210:43:210:104 | "SELECT * FROM users WHERE id ..." | This SQL query depends on a $@. | ActiveRecordInjection.rb:206:5:206:10 | call to params | user-provided value | -| ActiveRecordInjection.rb:211:35:211:96 | "SELECT * FROM users WHERE id ..." | ActiveRecordInjection.rb:206:5:206:10 | call to params | ActiveRecordInjection.rb:211:35:211:96 | "SELECT * FROM users WHERE id ..." | This SQL query depends on a $@. | ActiveRecordInjection.rb:206:5:206:10 | call to params | user-provided value | -| ActiveRecordInjection.rb:216:38:216:53 | "role = #{...}" | ActiveRecordInjection.rb:222:29:222:34 | call to params | ActiveRecordInjection.rb:216:38:216:53 | "role = #{...}" | This SQL query depends on a $@. | ActiveRecordInjection.rb:222:29:222:34 | call to params | user-provided value | -| ArelInjection.rb:6:20:6:61 | "SELECT * FROM users WHERE nam..." | ArelInjection.rb:4:12:4:17 | call to params | ArelInjection.rb:6:20:6:61 | "SELECT * FROM users WHERE nam..." | This SQL query depends on a $@. | ArelInjection.rb:4:12:4:17 | call to params | user-provided value | -| ArelInjection.rb:7:39:7:80 | "SELECT * FROM users WHERE nam..." | ArelInjection.rb:4:12:4:17 | call to params | ArelInjection.rb:7:39:7:80 | "SELECT * FROM users WHERE nam..." | This SQL query depends on a $@. | ArelInjection.rb:4:12:4:17 | call to params | user-provided value | -| PgInjection.rb:14:15:14:18 | qry1 | PgInjection.rb:6:12:6:17 | call to params | PgInjection.rb:14:15:14:18 | qry1 | This SQL query depends on a $@. | PgInjection.rb:6:12:6:17 | call to params | user-provided value | -| PgInjection.rb:15:21:15:24 | qry1 | PgInjection.rb:6:12:6:17 | call to params | PgInjection.rb:15:21:15:24 | qry1 | This SQL query depends on a $@. | PgInjection.rb:6:12:6:17 | call to params | user-provided value | -| PgInjection.rb:20:22:20:25 | qry2 | PgInjection.rb:6:12:6:17 | call to params | PgInjection.rb:20:22:20:25 | qry2 | This SQL query depends on a $@. | PgInjection.rb:6:12:6:17 | call to params | user-provided value | -| PgInjection.rb:21:28:21:31 | qry2 | PgInjection.rb:6:12:6:17 | call to params | PgInjection.rb:21:28:21:31 | qry2 | This SQL query depends on a $@. | PgInjection.rb:6:12:6:17 | call to params | user-provided value | -| PgInjection.rb:32:29:32:32 | qry3 | PgInjection.rb:6:12:6:17 | call to params | PgInjection.rb:32:29:32:32 | qry3 | This SQL query depends on a $@. | PgInjection.rb:6:12:6:17 | call to params | user-provided value | -| PgInjection.rb:44:29:44:32 | qry3 | PgInjection.rb:6:12:6:17 | call to params | PgInjection.rb:44:29:44:32 | qry3 | This SQL query depends on a $@. | PgInjection.rb:6:12:6:17 | call to params | user-provided value | diff --git a/ruby/ql/test/query-tests/security/cwe-089/SqlInjection.qlref b/ruby/ql/test/query-tests/security/cwe-089/SqlInjection.qlref index bcb55c8510f..7fb79e3340d 100644 --- a/ruby/ql/test/query-tests/security/cwe-089/SqlInjection.qlref +++ b/ruby/ql/test/query-tests/security/cwe-089/SqlInjection.qlref @@ -1 +1,2 @@ -queries/security/cwe-089/SqlInjection.ql +query: queries/security/cwe-089/SqlInjection.ql +postprocess: utils/test/InlineExpectationsTestQuery.ql diff --git a/ruby/ql/test/query-tests/security/cwe-094/UnsafeCodeConstruction/UnsafeCodeConstruction.expected b/ruby/ql/test/query-tests/security/cwe-094/UnsafeCodeConstruction/UnsafeCodeConstruction.expected index eae7c03a716..001b42c0caf 100644 --- a/ruby/ql/test/query-tests/security/cwe-094/UnsafeCodeConstruction/UnsafeCodeConstruction.expected +++ b/ruby/ql/test/query-tests/security/cwe-094/UnsafeCodeConstruction/UnsafeCodeConstruction.expected @@ -1,3 +1,15 @@ +#select +| impl/unsafeCode.rb:3:17:3:25 | #{...} | impl/unsafeCode.rb:2:12:2:17 | target | impl/unsafeCode.rb:3:17:3:25 | #{...} | This string interpolation which depends on $@ is later $@. | impl/unsafeCode.rb:2:12:2:17 | target | library input | impl/unsafeCode.rb:3:5:3:27 | call to eval | interpreted as code | +| impl/unsafeCode.rb:8:30:8:30 | x | impl/unsafeCode.rb:7:12:7:12 | x | impl/unsafeCode.rb:8:30:8:30 | x | This string format which depends on $@ is later $@. | impl/unsafeCode.rb:7:12:7:12 | x | library input | impl/unsafeCode.rb:8:5:8:32 | call to eval | interpreted as code | +| impl/unsafeCode.rb:13:33:13:33 | x | impl/unsafeCode.rb:12:12:12:12 | x | impl/unsafeCode.rb:13:33:13:33 | x | This string format which depends on $@ is later $@. | impl/unsafeCode.rb:12:12:12:12 | x | library input | impl/unsafeCode.rb:13:5:13:35 | call to eval | interpreted as code | +| impl/unsafeCode.rb:29:10:29:15 | my_arr | impl/unsafeCode.rb:28:17:28:22 | my_arr | impl/unsafeCode.rb:29:10:29:15 | my_arr | This array which depends on $@ is later $@. | impl/unsafeCode.rb:28:17:28:22 | my_arr | library input | impl/unsafeCode.rb:29:5:29:27 | call to eval | interpreted as code | +| impl/unsafeCode.rb:34:10:34:12 | arr | impl/unsafeCode.rb:32:21:32:21 | x | impl/unsafeCode.rb:34:10:34:12 | arr | This array which depends on $@ is later $@. | impl/unsafeCode.rb:32:21:32:21 | x | library input | impl/unsafeCode.rb:34:5:34:24 | call to eval | interpreted as code | +| impl/unsafeCode.rb:40:10:40:12 | arr | impl/unsafeCode.rb:37:15:37:15 | x | impl/unsafeCode.rb:40:10:40:12 | arr | This array which depends on $@ is later $@. | impl/unsafeCode.rb:37:15:37:15 | x | library input | impl/unsafeCode.rb:40:5:40:24 | call to eval | interpreted as code | +| impl/unsafeCode.rb:44:10:44:12 | arr | impl/unsafeCode.rb:37:15:37:15 | x | impl/unsafeCode.rb:44:10:44:12 | arr | This array which depends on $@ is later $@. | impl/unsafeCode.rb:37:15:37:15 | x | library input | impl/unsafeCode.rb:44:5:44:24 | call to eval | interpreted as code | +| impl/unsafeCode.rb:49:9:49:12 | #{...} | impl/unsafeCode.rb:47:15:47:15 | x | impl/unsafeCode.rb:49:9:49:12 | #{...} | This string interpolation which depends on $@ is later $@. | impl/unsafeCode.rb:47:15:47:15 | x | library input | impl/unsafeCode.rb:52:5:52:13 | call to eval | interpreted as code | +| impl/unsafeCode.rb:56:22:56:22 | x | impl/unsafeCode.rb:55:21:55:21 | x | impl/unsafeCode.rb:56:22:56:22 | x | This string concatenation which depends on $@ is later $@. | impl/unsafeCode.rb:55:21:55:21 | x | library input | impl/unsafeCode.rb:57:5:57:13 | call to eval | interpreted as code | +| impl/unsafeCode.rb:62:10:62:12 | arr | impl/unsafeCode.rb:60:21:60:21 | x | impl/unsafeCode.rb:62:10:62:12 | arr | This array which depends on $@ is later $@. | impl/unsafeCode.rb:60:21:60:21 | x | library input | impl/unsafeCode.rb:62:5:62:23 | call to eval | interpreted as code | +| impl/unsafeCode.rb:65:10:65:13 | arr2 | impl/unsafeCode.rb:60:24:60:24 | y | impl/unsafeCode.rb:65:10:65:13 | arr2 | This array which depends on $@ is later $@. | impl/unsafeCode.rb:60:24:60:24 | y | library input | impl/unsafeCode.rb:65:5:65:25 | call to eval | interpreted as code | edges | impl/unsafeCode.rb:2:12:2:17 | target | impl/unsafeCode.rb:3:17:3:25 | #{...} | provenance | | | impl/unsafeCode.rb:7:12:7:12 | x | impl/unsafeCode.rb:8:30:8:30 | x | provenance | | @@ -12,18 +24,18 @@ edges | impl/unsafeCode.rb:39:5:39:7 | [post] arr : [collection] [element] | impl/unsafeCode.rb:44:10:44:12 | arr | provenance | | | impl/unsafeCode.rb:39:14:39:14 | x | impl/unsafeCode.rb:39:5:39:7 | [post] arr : [collection] [element] | provenance | | | impl/unsafeCode.rb:47:15:47:15 | x | impl/unsafeCode.rb:49:9:49:12 | #{...} | provenance | | -| impl/unsafeCode.rb:54:21:54:21 | x | impl/unsafeCode.rb:55:22:55:22 | x | provenance | | -| impl/unsafeCode.rb:59:21:59:21 | x | impl/unsafeCode.rb:60:17:60:17 | x | provenance | | -| impl/unsafeCode.rb:59:24:59:24 | y | impl/unsafeCode.rb:63:30:63:30 | y | provenance | | -| impl/unsafeCode.rb:60:5:60:7 | arr : [collection] [element 0] | impl/unsafeCode.rb:61:10:61:12 | arr | provenance | | -| impl/unsafeCode.rb:60:11:60:18 | call to Array : [collection] [element 0] | impl/unsafeCode.rb:60:5:60:7 | arr : [collection] [element 0] | provenance | | -| impl/unsafeCode.rb:60:17:60:17 | x | impl/unsafeCode.rb:60:11:60:18 | call to Array : [collection] [element 0] | provenance | | -| impl/unsafeCode.rb:63:5:63:8 | arr2 : Array [element 0] | impl/unsafeCode.rb:64:10:64:13 | arr2 | provenance | | -| impl/unsafeCode.rb:63:12:63:43 | call to [] : Array [element 0] | impl/unsafeCode.rb:63:5:63:8 | arr2 : Array [element 0] | provenance | | -| impl/unsafeCode.rb:63:13:63:32 | call to Array : Array [element 1] | impl/unsafeCode.rb:63:13:63:42 | call to join | provenance | | -| impl/unsafeCode.rb:63:13:63:42 | call to join | impl/unsafeCode.rb:63:12:63:43 | call to [] : Array [element 0] | provenance | | -| impl/unsafeCode.rb:63:19:63:31 | call to [] : Array [element 1] | impl/unsafeCode.rb:63:13:63:32 | call to Array : Array [element 1] | provenance | | -| impl/unsafeCode.rb:63:30:63:30 | y | impl/unsafeCode.rb:63:19:63:31 | call to [] : Array [element 1] | provenance | | +| impl/unsafeCode.rb:55:21:55:21 | x | impl/unsafeCode.rb:56:22:56:22 | x | provenance | | +| impl/unsafeCode.rb:60:21:60:21 | x | impl/unsafeCode.rb:61:17:61:17 | x | provenance | | +| impl/unsafeCode.rb:60:24:60:24 | y | impl/unsafeCode.rb:64:30:64:30 | y | provenance | | +| impl/unsafeCode.rb:61:5:61:7 | arr : [collection] [element 0] | impl/unsafeCode.rb:62:10:62:12 | arr | provenance | | +| impl/unsafeCode.rb:61:11:61:18 | call to Array : [collection] [element 0] | impl/unsafeCode.rb:61:5:61:7 | arr : [collection] [element 0] | provenance | | +| impl/unsafeCode.rb:61:17:61:17 | x | impl/unsafeCode.rb:61:11:61:18 | call to Array : [collection] [element 0] | provenance | | +| impl/unsafeCode.rb:64:5:64:8 | arr2 : Array [element 0] | impl/unsafeCode.rb:65:10:65:13 | arr2 | provenance | | +| impl/unsafeCode.rb:64:12:64:43 | call to [] : Array [element 0] | impl/unsafeCode.rb:64:5:64:8 | arr2 : Array [element 0] | provenance | | +| impl/unsafeCode.rb:64:13:64:32 | call to Array : Array [element 1] | impl/unsafeCode.rb:64:13:64:42 | call to join | provenance | | +| impl/unsafeCode.rb:64:13:64:42 | call to join | impl/unsafeCode.rb:64:12:64:43 | call to [] : Array [element 0] | provenance | | +| impl/unsafeCode.rb:64:19:64:31 | call to [] : Array [element 1] | impl/unsafeCode.rb:64:13:64:32 | call to Array : Array [element 1] | provenance | | +| impl/unsafeCode.rb:64:30:64:30 | y | impl/unsafeCode.rb:64:19:64:31 | call to [] : Array [element 1] | provenance | | nodes | impl/unsafeCode.rb:2:12:2:17 | target | semmle.label | target | | impl/unsafeCode.rb:3:17:3:25 | #{...} | semmle.label | #{...} | @@ -45,31 +57,19 @@ nodes | impl/unsafeCode.rb:44:10:44:12 | arr | semmle.label | arr | | impl/unsafeCode.rb:47:15:47:15 | x | semmle.label | x | | impl/unsafeCode.rb:49:9:49:12 | #{...} | semmle.label | #{...} | -| impl/unsafeCode.rb:54:21:54:21 | x | semmle.label | x | -| impl/unsafeCode.rb:55:22:55:22 | x | semmle.label | x | -| impl/unsafeCode.rb:59:21:59:21 | x | semmle.label | x | -| impl/unsafeCode.rb:59:24:59:24 | y | semmle.label | y | -| impl/unsafeCode.rb:60:5:60:7 | arr : [collection] [element 0] | semmle.label | arr : [collection] [element 0] | -| impl/unsafeCode.rb:60:11:60:18 | call to Array : [collection] [element 0] | semmle.label | call to Array : [collection] [element 0] | -| impl/unsafeCode.rb:60:17:60:17 | x | semmle.label | x | -| impl/unsafeCode.rb:61:10:61:12 | arr | semmle.label | arr | -| impl/unsafeCode.rb:63:5:63:8 | arr2 : Array [element 0] | semmle.label | arr2 : Array [element 0] | -| impl/unsafeCode.rb:63:12:63:43 | call to [] : Array [element 0] | semmle.label | call to [] : Array [element 0] | -| impl/unsafeCode.rb:63:13:63:32 | call to Array : Array [element 1] | semmle.label | call to Array : Array [element 1] | -| impl/unsafeCode.rb:63:13:63:42 | call to join | semmle.label | call to join | -| impl/unsafeCode.rb:63:19:63:31 | call to [] : Array [element 1] | semmle.label | call to [] : Array [element 1] | -| impl/unsafeCode.rb:63:30:63:30 | y | semmle.label | y | -| impl/unsafeCode.rb:64:10:64:13 | arr2 | semmle.label | arr2 | +| impl/unsafeCode.rb:55:21:55:21 | x | semmle.label | x | +| impl/unsafeCode.rb:56:22:56:22 | x | semmle.label | x | +| impl/unsafeCode.rb:60:21:60:21 | x | semmle.label | x | +| impl/unsafeCode.rb:60:24:60:24 | y | semmle.label | y | +| impl/unsafeCode.rb:61:5:61:7 | arr : [collection] [element 0] | semmle.label | arr : [collection] [element 0] | +| impl/unsafeCode.rb:61:11:61:18 | call to Array : [collection] [element 0] | semmle.label | call to Array : [collection] [element 0] | +| impl/unsafeCode.rb:61:17:61:17 | x | semmle.label | x | +| impl/unsafeCode.rb:62:10:62:12 | arr | semmle.label | arr | +| impl/unsafeCode.rb:64:5:64:8 | arr2 : Array [element 0] | semmle.label | arr2 : Array [element 0] | +| impl/unsafeCode.rb:64:12:64:43 | call to [] : Array [element 0] | semmle.label | call to [] : Array [element 0] | +| impl/unsafeCode.rb:64:13:64:32 | call to Array : Array [element 1] | semmle.label | call to Array : Array [element 1] | +| impl/unsafeCode.rb:64:13:64:42 | call to join | semmle.label | call to join | +| impl/unsafeCode.rb:64:19:64:31 | call to [] : Array [element 1] | semmle.label | call to [] : Array [element 1] | +| impl/unsafeCode.rb:64:30:64:30 | y | semmle.label | y | +| impl/unsafeCode.rb:65:10:65:13 | arr2 | semmle.label | arr2 | subpaths -#select -| impl/unsafeCode.rb:3:17:3:25 | #{...} | impl/unsafeCode.rb:2:12:2:17 | target | impl/unsafeCode.rb:3:17:3:25 | #{...} | This string interpolation which depends on $@ is later $@. | impl/unsafeCode.rb:2:12:2:17 | target | library input | impl/unsafeCode.rb:3:5:3:27 | call to eval | interpreted as code | -| impl/unsafeCode.rb:8:30:8:30 | x | impl/unsafeCode.rb:7:12:7:12 | x | impl/unsafeCode.rb:8:30:8:30 | x | This string format which depends on $@ is later $@. | impl/unsafeCode.rb:7:12:7:12 | x | library input | impl/unsafeCode.rb:8:5:8:32 | call to eval | interpreted as code | -| impl/unsafeCode.rb:13:33:13:33 | x | impl/unsafeCode.rb:12:12:12:12 | x | impl/unsafeCode.rb:13:33:13:33 | x | This string format which depends on $@ is later $@. | impl/unsafeCode.rb:12:12:12:12 | x | library input | impl/unsafeCode.rb:13:5:13:35 | call to eval | interpreted as code | -| impl/unsafeCode.rb:29:10:29:15 | my_arr | impl/unsafeCode.rb:28:17:28:22 | my_arr | impl/unsafeCode.rb:29:10:29:15 | my_arr | This array which depends on $@ is later $@. | impl/unsafeCode.rb:28:17:28:22 | my_arr | library input | impl/unsafeCode.rb:29:5:29:27 | call to eval | interpreted as code | -| impl/unsafeCode.rb:34:10:34:12 | arr | impl/unsafeCode.rb:32:21:32:21 | x | impl/unsafeCode.rb:34:10:34:12 | arr | This array which depends on $@ is later $@. | impl/unsafeCode.rb:32:21:32:21 | x | library input | impl/unsafeCode.rb:34:5:34:24 | call to eval | interpreted as code | -| impl/unsafeCode.rb:40:10:40:12 | arr | impl/unsafeCode.rb:37:15:37:15 | x | impl/unsafeCode.rb:40:10:40:12 | arr | This array which depends on $@ is later $@. | impl/unsafeCode.rb:37:15:37:15 | x | library input | impl/unsafeCode.rb:40:5:40:24 | call to eval | interpreted as code | -| impl/unsafeCode.rb:44:10:44:12 | arr | impl/unsafeCode.rb:37:15:37:15 | x | impl/unsafeCode.rb:44:10:44:12 | arr | This array which depends on $@ is later $@. | impl/unsafeCode.rb:37:15:37:15 | x | library input | impl/unsafeCode.rb:44:5:44:24 | call to eval | interpreted as code | -| impl/unsafeCode.rb:49:9:49:12 | #{...} | impl/unsafeCode.rb:47:15:47:15 | x | impl/unsafeCode.rb:49:9:49:12 | #{...} | This string interpolation which depends on $@ is later $@. | impl/unsafeCode.rb:47:15:47:15 | x | library input | impl/unsafeCode.rb:51:5:51:13 | call to eval | interpreted as code | -| impl/unsafeCode.rb:55:22:55:22 | x | impl/unsafeCode.rb:54:21:54:21 | x | impl/unsafeCode.rb:55:22:55:22 | x | This string concatenation which depends on $@ is later $@. | impl/unsafeCode.rb:54:21:54:21 | x | library input | impl/unsafeCode.rb:56:5:56:13 | call to eval | interpreted as code | -| impl/unsafeCode.rb:61:10:61:12 | arr | impl/unsafeCode.rb:59:21:59:21 | x | impl/unsafeCode.rb:61:10:61:12 | arr | This array which depends on $@ is later $@. | impl/unsafeCode.rb:59:21:59:21 | x | library input | impl/unsafeCode.rb:61:5:61:23 | call to eval | interpreted as code | -| impl/unsafeCode.rb:64:10:64:13 | arr2 | impl/unsafeCode.rb:59:24:59:24 | y | impl/unsafeCode.rb:64:10:64:13 | arr2 | This array which depends on $@ is later $@. | impl/unsafeCode.rb:59:24:59:24 | y | library input | impl/unsafeCode.rb:64:5:64:25 | call to eval | interpreted as code | diff --git a/ruby/ql/test/query-tests/security/cwe-094/UnsafeCodeConstruction/UnsafeCodeConstruction.qlref b/ruby/ql/test/query-tests/security/cwe-094/UnsafeCodeConstruction/UnsafeCodeConstruction.qlref index ec336901db5..184c870500d 100644 --- a/ruby/ql/test/query-tests/security/cwe-094/UnsafeCodeConstruction/UnsafeCodeConstruction.qlref +++ b/ruby/ql/test/query-tests/security/cwe-094/UnsafeCodeConstruction/UnsafeCodeConstruction.qlref @@ -1 +1,2 @@ -queries/security/cwe-094/UnsafeCodeConstruction.ql \ No newline at end of file +query: queries/security/cwe-094/UnsafeCodeConstruction.ql +postprocess: utils/test/InlineExpectationsTestQuery.ql diff --git a/ruby/ql/test/query-tests/security/cwe-094/UnsafeCodeConstruction/impl/unsafeCode.rb b/ruby/ql/test/query-tests/security/cwe-094/UnsafeCodeConstruction/impl/unsafeCode.rb index b69048f6328..b0f623c4224 100644 --- a/ruby/ql/test/query-tests/security/cwe-094/UnsafeCodeConstruction/impl/unsafeCode.rb +++ b/ruby/ql/test/query-tests/security/cwe-094/UnsafeCodeConstruction/impl/unsafeCode.rb @@ -1,17 +1,17 @@ class Foobar - def foo1(target) - eval("foo = #{target}") # NOT OK + def foo1(target) # $ Source + eval("foo = #{target}") # $ Alert // NOT OK end # sprintf - def foo2(x) - eval(sprintf("foo = %s", x)) # NOT OK + def foo2(x) # $ Source + eval(sprintf("foo = %s", x)) # $ Alert // NOT OK end # String#% - def foo3(x) - eval("foo = %{foo}" % {foo: x}) # NOT OK - end + def foo3(x) # $ Source + eval("foo = %{foo}" % {foo: x}) # $ Alert // NOT OK + end def indirect_eval(x) eval(x) # OK - no construction. @@ -25,42 +25,43 @@ class Foobar eval("def \n #{code} \n end") # OK - parameter is named code end - def joinStuff(my_arr) - eval(my_arr.join("\n")) # NOT OK + def joinStuff(my_arr) # $ Source + eval(my_arr.join("\n")) # $ Alert // NOT OK end - def joinWithElemt(x) + def joinWithElemt(x) # $ Source arr = [x, "foobar"] - eval(arr.join("\n")) # NOT OK + eval(arr.join("\n")) # $ Alert // NOT OK end - def pushArr(x, y) + def pushArr(x, y) # $ Source arr = [] arr.push(x) - eval(arr.join("\n")) # NOT OK + eval(arr.join("\n")) # $ Alert // NOT OK arr2 = [] arr2 << y - eval(arr.join("\n")) # NOT OK + eval(arr.join("\n")) # $ Alert // NOT OK end - def hereDoc(x) + def hereDoc(x) # $ Source foo = <<~HERE - #{x} + #{x} #{# $ Alert +} HERE eval(foo) # NOT OK end - def string_concat(x) - foo = "foo = " + x + def string_concat(x) # $ Source + foo = "foo = " + x # $ Alert eval(foo) # NOT OK end - def join_indirect(x, y) + def join_indirect(x, y) # $ Source arr = Array(x) - eval(arr.join(" ")) # NOT OK + eval(arr.join(" ")) # $ Alert // NOT OK arr2 = [Array(["foo = ", y]).join(" ")] - eval(arr2.join("\n")) # NOT OK + eval(arr2.join("\n")) # $ Alert // NOT OK end end diff --git a/ruby/ql/test/query-tests/security/cwe-116/BadTagFilter/BadTagFilter.qlref b/ruby/ql/test/query-tests/security/cwe-116/BadTagFilter/BadTagFilter.qlref index 6780ef6d4c8..d0ba313d71e 100644 --- a/ruby/ql/test/query-tests/security/cwe-116/BadTagFilter/BadTagFilter.qlref +++ b/ruby/ql/test/query-tests/security/cwe-116/BadTagFilter/BadTagFilter.qlref @@ -1 +1,2 @@ -queries/security/cwe-116/BadTagFilter.ql \ No newline at end of file +query: queries/security/cwe-116/BadTagFilter.ql +postprocess: utils/test/InlineExpectationsTestQuery.ql diff --git a/ruby/ql/test/query-tests/security/cwe-116/BadTagFilter/test.rb b/ruby/ql/test/query-tests/security/cwe-116/BadTagFilter/test.rb index dd4a074c784..8dc78ea00bd 100644 --- a/ruby/ql/test/query-tests/security/cwe-116/BadTagFilter/test.rb +++ b/ruby/ql/test/query-tests/security/cwe-116/BadTagFilter/test.rb @@ -1,22 +1,22 @@ filters = [ - /.*?<\/script>/i, # NOT OK - doesn't match newlines or `` - /.*?<\/script>/im, # NOT OK - doesn't match `` + /.*?<\/script>/i, # $ Alert // NOT OK - doesn't match newlines or `` + /.*?<\/script>/im, # $ Alert // NOT OK - doesn't match `` /.*?<\/script[^>]*>/im, # OK //im, # OK - we don't care regexps that only match comments /)|([^\/\s>]+)[\S\s]*?>/, # NOT OK - doesn't match comments with the right capture groups - /<(?:(?:\/([^>]+)>)|(?:!--([\S|\s]*?)-->)|(?:([^\/\s>]+)((?:\s+[\w\-:.]+(?:\s*=\s*?(?:(?:"[^"]*")|(?:'[^']*')|[^\s"'\/>]+))?)*)[\S\s]*?(\/?)>))/, # NOT OK - capture groups + /]*>([\s\S]*?)<\/script>/gi, # $ Alert // NOT OK - too strict matching on the end tag + /<(?:!--([\S|\s]*?)-->)|([^\/\s>]+)[\S\s]*?>/, # $ Alert // NOT OK - doesn't match comments with the right capture groups + /<(?:(?:\/([^>]+)>)|(?:!--([\S|\s]*?)-->)|(?:([^\/\s>]+)((?:\s+[\w\-:.]+(?:\s*=\s*?(?:(?:"[^"]*")|(?:'[^']*')|[^\s"'\/>]+))?)*)[\S\s]*?(\/?)>))/, # $ Alert // NOT OK - capture groups ] -doFilters(filters) \ No newline at end of file +doFilters(filters) diff --git a/ruby/ql/test/query-tests/security/cwe-116/IncompleteSanitization/IncompleteSanitization.qlref b/ruby/ql/test/query-tests/security/cwe-116/IncompleteSanitization/IncompleteSanitization.qlref index 966c74aaf64..e7f5463e794 100644 --- a/ruby/ql/test/query-tests/security/cwe-116/IncompleteSanitization/IncompleteSanitization.qlref +++ b/ruby/ql/test/query-tests/security/cwe-116/IncompleteSanitization/IncompleteSanitization.qlref @@ -1 +1,2 @@ -queries/security/cwe-116/IncompleteSanitization.ql \ No newline at end of file +query: queries/security/cwe-116/IncompleteSanitization.ql +postprocess: utils/test/InlineExpectationsTestQuery.ql diff --git a/ruby/ql/test/query-tests/security/cwe-116/IncompleteSanitization/tst.rb b/ruby/ql/test/query-tests/security/cwe-116/IncompleteSanitization/tst.rb index f59fdd332ae..0fddda9a6d5 100644 --- a/ruby/ql/test/query-tests/security/cwe-116/IncompleteSanitization/tst.rb +++ b/ruby/ql/test/query-tests/security/cwe-116/IncompleteSanitization/tst.rb @@ -1,91 +1,91 @@ def bad1(s) - s.sub "'", "" # NOT OK - s.sub! "'", "" # NOT OK + s.sub "'", "" # $ Alert // NOT OK + s.sub! "'", "" # $ Alert // NOT OK end def bad2(s) - s.sub /'/, "" # NOT OK - s.sub! /'/, "" # NOT OK + s.sub /'/, "" # $ Alert // NOT OK + s.sub! /'/, "" # $ Alert // NOT OK end def bad3(s1, s2, s3) - s1.gsub /'/, "\\'" # NOT OK - s1.gsub /'/, '\\\'' # NOT OK - s2.gsub! /'/, "\\'" # NOT OK - s3.gsub! /'/, '\\\'' # NOT OK + s1.gsub /'/, "\\'" # $ Alert // NOT OK + s1.gsub /'/, '\\\'' # $ Alert // NOT OK + s2.gsub! /'/, "\\'" # $ Alert // NOT OK + s3.gsub! /'/, '\\\'' # $ Alert // NOT OK end def bad4(s1, s2, s3) - s1.gsub /'/, "\\\\\\&" # NOT OK - s1.gsub /'/, '\\\\\&' # NOT OK - s2.gsub! /'/, "\\\\\\&" # NOT OK - s3.gsub! /'/, '\\\\\&' # NOT OK + s1.gsub /'/, "\\\\\\&" # $ Alert // NOT OK + s1.gsub /'/, '\\\\\&' # $ Alert // NOT OK + s2.gsub! /'/, "\\\\\\&" # $ Alert // NOT OK + s3.gsub! /'/, '\\\\\&' # $ Alert // NOT OK end def bad5(s) - s.gsub /['"]/, '\\\\\&' # NOT OK - s.gsub! /['"]/, '\\\\\&' # NOT OK + s.gsub /['"]/, '\\\\\&' # $ Alert // NOT OK + s.gsub! /['"]/, '\\\\\&' # $ Alert // NOT OK end def bad6(s) - s.gsub /(['"])/, '\\\\\\1' # NOT OK - s.gsub! /(['"])/, '\\\\\\1' # NOT OK + s.gsub /(['"])/, '\\\\\\1' # $ Alert // NOT OK + s.gsub! /(['"])/, '\\\\\\1' # $ Alert // NOT OK end def bad7(s) - s.gsub /('|")/, '\\\\\1' # NOT OK - s.gsub! /('|")/, '\\\\\1' # NOT OK + s.gsub /('|")/, '\\\\\1' # $ Alert // NOT OK + s.gsub! /('|")/, '\\\\\1' # $ Alert // NOT OK end def bad8(s) - s.sub '|', '' # NOT OK - s.sub! '|', '' # NOT OK + s.sub '|', '' # $ Alert // NOT OK + s.sub! '|', '' # $ Alert // NOT OK end def bad9(s1, s2, s3, s4) - s1.gsub /"/, "\\\"" # NOT OK - s1.gsub /"/, '\\"' # NOT OK - s1.gsub '"', '\\"' # NOT OK - s2.gsub! /"/, "\\\"" # NOT OK - s3.gsub! /"/, '\\"' # NOT OK - s4.gsub! '"', '\\"' # NOT OK + s1.gsub /"/, "\\\"" # $ Alert // NOT OK + s1.gsub /"/, '\\"' # $ Alert // NOT OK + s1.gsub '"', '\\"' # $ Alert // NOT OK + s2.gsub! /"/, "\\\"" # $ Alert // NOT OK + s3.gsub! /"/, '\\"' # $ Alert // NOT OK + s4.gsub! '"', '\\"' # $ Alert // NOT OK end def bad10(s) - s.sub "/", "%2F" # NOT OK - s.sub! "/", "%2F" # NOT OK + s.sub "/", "%2F" # $ Alert // NOT OK + s.sub! "/", "%2F" # $ Alert // NOT OK end def bad11(s) - s.sub "%25", "%" # NOT OK - s.sub! "%25", "%" # NOT OK + s.sub "%25", "%" # $ Alert // NOT OK + s.sub! "%25", "%" # $ Alert // NOT OK end def bad12(s) - s.sub %q['], %q[] # NOT OK - s.sub! %q['], %q[] # NOT OK + s.sub %q['], %q[] # $ Alert // NOT OK + s.sub! %q['], %q[] # $ Alert // NOT OK end def bad13(s) - s.sub "'" + "", "" # NOT OK - s.sub! "'" + "", "" # NOT OK + s.sub "'" + "", "" # $ Alert // NOT OK + s.sub! "'" + "", "" # $ Alert // NOT OK end def bad14(s) - s.sub "'", "" + "" # NOT OK - s.sub! "'", "" + "" # NOT OK + s.sub "'", "" + "" # $ Alert // NOT OK + s.sub! "'", "" + "" # $ Alert // NOT OK end def bad15(s) - s.sub "'" + "", "" + "" # NOT OK - s.sub! "'" + "", "" + "" # NOT OK + s.sub "'" + "", "" + "" # $ Alert // NOT OK + s.sub! "'" + "", "" + "" # $ Alert // NOT OK end def bad16(s) indirect = /'/ - s.sub(indirect, "") # NOT OK - s.sub!(indirect, "") # NOT OK + s.sub(indirect, "") # $ Alert // NOT OK + s.sub!(indirect, "") # $ Alert // NOT OK end def good1a(s) @@ -212,15 +212,15 @@ def good13a(s) s.sub('[', '').sub(']', '') # OK s.sub('(', '').sub(')', '') # OK s.sub('{', '').sub('}', '') # OK - s.sub('<', '').sub('>', '') # NOT OK: too common as a bad HTML sanitizer + s.sub('<', '').sub('>', '') # $ Alert // NOT OK: too common as a bad HTML sanitizer - s.sub('[', '\\[').sub(']', '\\]') # NOT OK - s.sub('{', '\\{').sub('}', '\\}') # NOT OK + s.sub('[', '\\[').sub(']', '\\]') # $ Alert // NOT OK + s.sub('{', '\\{').sub('}', '\\}') # $ Alert // NOT OK s = s.sub('[', '') # OK s = s.sub(']', '') # OK s.sub(/{/, '').sub(/}/, '') # OK - s.sub(']', '').sub('[', '') # probably OK, but still flagged + s.sub(']', '').sub('[', '') # $ SPURIOUS: Alert // probably OK, but still flagged end def good13b(s1) @@ -245,8 +245,8 @@ def newlines_a(a, b, c) # motivation for whitelist `which emacs`.sub("\n", "") # OK - a.sub("\n", "").sub(b, c) # NOT OK - a.sub(b, c).sub("\n", "") # NOT OK + a.sub("\n", "").sub(b, c) # $ Alert // NOT OK + a.sub(b, c).sub("\n", "") # $ Alert // NOT OK end def newlines_b(a, b, c) @@ -255,18 +255,18 @@ def newlines_b(a, b, c) output.sub!("\n", "") # OK d = a.dup - d.sub!("\n", "") # NOT OK + d.sub!("\n", "") # $ Alert // NOT OK d.sub!(b, c) e = a.dup d.sub!(b, c) - d.sub!("\n", "") # NOT OK + d.sub!("\n", "") # $ Alert // NOT OK end def bad_path_sanitizer(p1, p2) # attempt at path sanitization - p1.sub! "/../", "" # NOT OK - p2.sub "/../", "" # NOT OK + p1.sub! "/../", "" # $ Alert // NOT OK + p2.sub "/../", "" # $ Alert // NOT OK end def each_line_sanitizer(p1) diff --git a/ruby/ql/test/query-tests/security/cwe-117/LogInjection.qlref b/ruby/ql/test/query-tests/security/cwe-117/LogInjection.qlref index 3368edec402..19ed712f458 100644 --- a/ruby/ql/test/query-tests/security/cwe-117/LogInjection.qlref +++ b/ruby/ql/test/query-tests/security/cwe-117/LogInjection.qlref @@ -1 +1,2 @@ -queries/security/cwe-117/LogInjection.ql \ No newline at end of file +query: queries/security/cwe-117/LogInjection.ql +postprocess: utils/test/InlineExpectationsTestQuery.ql diff --git a/ruby/ql/test/query-tests/security/cwe-117/app/controllers/users_controller.rb b/ruby/ql/test/query-tests/security/cwe-117/app/controllers/users_controller.rb index 67e0e1cb1a7..29fafb46f78 100644 --- a/ruby/ql/test/query-tests/security/cwe-117/app/controllers/users_controller.rb +++ b/ruby/ql/test/query-tests/security/cwe-117/app/controllers/users_controller.rb @@ -12,9 +12,9 @@ class UsersController < ApplicationController def read_from_params init_logger - unsanitized = params[:foo] - @logger.debug unsanitized # BAD: unsanitized user input - @logger.error "input: " + unsanitized # BAD: unsanitized user input + unsanitized = params[:foo] # $ Source + @logger.debug unsanitized # $ Alert // BAD: unsanitized user input + @logger.error "input: " + unsanitized # $ Alert // BAD: unsanitized user input sanitized = unsanitized.gsub("\n", "") @logger.fatal sanitized # GOOD: sanitized user input @@ -22,17 +22,17 @@ class UsersController < ApplicationController unsanitized2 = unsanitized.sub("\n", "") @logger.info do - unsanitized2 # BAD: partially sanitized user input + unsanitized2 # $ Alert // BAD: partially sanitized user input end - @logger << "input: " + unsanitized2 # BAD: partially sanitized user input + @logger << "input: " + unsanitized2 # $ Alert // BAD: partially sanitized user input end def read_from_cookies init_logger - unsanitized = cookies[:bar] - @logger.add(Logger::INFO) { unsanitized } # BAD: unsanitized user input - @logger.log(Logger::WARN) { "input: " + unsanitized } # BAD: unsanitized user input + unsanitized = cookies[:bar] # $ Source + @logger.add(Logger::INFO) { unsanitized } # $ Alert // BAD: unsanitized user input + @logger.log(Logger::WARN) { "input: " + unsanitized } # $ Alert // BAD: unsanitized user input end def html_sanitization @@ -46,7 +46,7 @@ class UsersController < ApplicationController def inspect_sanitization init_logger - @logger.debug params[:foo] # BAD: unsanitized user input + @logger.debug params[:foo] # $ Alert // BAD: unsanitized user input @logger.debug params[:foo].inspect # GOOD: sanitized user input end end diff --git a/ruby/ql/test/query-tests/security/cwe-1333-exponential-redos/ReDoS.qlref b/ruby/ql/test/query-tests/security/cwe-1333-exponential-redos/ReDoS.qlref index 7f4557181d7..12b80689587 100644 --- a/ruby/ql/test/query-tests/security/cwe-1333-exponential-redos/ReDoS.qlref +++ b/ruby/ql/test/query-tests/security/cwe-1333-exponential-redos/ReDoS.qlref @@ -1 +1,2 @@ -queries/security/cwe-1333/ReDoS.ql +query: queries/security/cwe-1333/ReDoS.ql +postprocess: utils/test/InlineExpectationsTestQuery.ql diff --git a/ruby/ql/test/query-tests/security/cwe-1333-exponential-redos/tst.rb b/ruby/ql/test/query-tests/security/cwe-1333-exponential-redos/tst.rb index 450d330dc92..0cac356ea20 100644 --- a/ruby/ql/test/query-tests/security/cwe-1333-exponential-redos/tst.rb +++ b/ruby/ql/test/query-tests/security/cwe-1333-exponential-redos/tst.rb @@ -1,7 +1,7 @@ # NOT GOOD; attack: "_" + "__".repeat(100) # Adapted from marked (https://github.com/markedjs/marked), which is licensed # under the MIT license; see file marked-LICENSE. -bad1 = /^\b_((?:__|[\s\S])+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/ +bad1 = /^\b_((?:__|[\s\S])+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/ # $ Alert # GOOD # Adapted from marked (https://github.com/markedjs/marked), which is licensed @@ -16,7 +16,7 @@ good2 = /(.*,)+.+/ # NOT GOOD; attack: " '" + "\\\\".repeat(100) # Adapted from CodeMirror (https://github.com/codemirror/codemirror), # which is licensed under the MIT license; see file CodeMirror-LICENSE. -bad2 = /^(?:\s+(?:"(?:[^"\\]|\\\\|\\.)+"|'(?:[^'\\]|\\\\|\\.)+'|\((?:[^)\\]|\\\\|\\.)+\)))?/ +bad2 = /^(?:\s+(?:"(?:[^"\\]|\\\\|\\.)+"|'(?:[^'\\]|\\\\|\\.)+'|\((?:[^)\\]|\\\\|\\.)+\)))?/ # $ Alert # GOOD # Adapted from lulucms2 (https://github.com/yiifans/lulucms2). @@ -28,89 +28,89 @@ good2 = /\(\*(?:[\s\S]*?\(\*[\s\S]*?\*\))*[\s\S]*?\*\)/ good3 = /^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/ # NOT GOOD, variant of good3; attack: "a|\n:|\n" + "||\n".repeat(100) -bad4 = /^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)a/ +bad4 = /^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)a/ # $ Alert # NOT GOOD; attack: "/" + "\\/a".repeat(100) # Adapted from ANodeBlog (https://github.com/gefangshuai/ANodeBlog), # which is licensed under the Apache License 2.0; see file ANodeBlog-LICENSE. -bad5 = /\/(?![ *])(\\\/|.)*?\/[gim]*(?=\W|$)/ +bad5 = /\/(?![ *])(\\\/|.)*?\/[gim]*(?=\W|$)/ # $ Alert # NOT GOOD; attack: "##".repeat(100) + "\na" # Adapted from CodeMirror (https://github.com/codemirror/codemirror), # which is licensed under the MIT license; see file CodeMirror-LICENSE. -bad6 = /^([\s\[\{\(]|#.*)*$/ +bad6 = /^([\s\[\{\(]|#.*)*$/ # $ Alert # GOOD good4 = /(\r\n|\r|\n)+/ # BAD - PoC: `node -e "/((?:[^\"\']|\".*?\"|\'.*?\')*?)([(,)]|$)/.test(\"'''''''''''''''''''''''''''''''''''''''''''''\\\"\");"`. It's complicated though, because the regexp still matches something, it just matches the empty-string after the attack string. -actuallyBad = /((?:[^"']|".*?"|'.*?')*?)([(,)]|$)/ +actuallyBad = /((?:[^"']|".*?"|'.*?')*?)([(,)]|$)/ # $ Alert # NOT GOOD; attack: "a" + "[]".repeat(100) + ".b\n" # Adapted from Knockout (https://github.com/knockout/knockout), which is # licensed under the MIT license; see file knockout-LICENSE -bad6 = /^[\_$a-z][\_$a-z0-9]*(\[.*?\])*(\.[\_$a-z][\_$a-z0-9]*(\[.*?\])*)*$/i +bad6 = /^[\_$a-z][\_$a-z0-9]*(\[.*?\])*(\.[\_$a-z][\_$a-z0-9]*(\[.*?\])*)*$/i # $ Alert # GOOD good6 = /(a|.)*/ # Testing the NFA - only some of the below are detected. -bad7 = /^([a-z]+)+$/ -bad8 = /^([a-z]*)*$/ -bad9 = /^([a-zA-Z0-9])(([\\.-]|[_]+)?([a-zA-Z0-9]+))*(@){1}[a-z0-9]+[.]{1}(([a-z]{2,3})|([a-z]{2,3}[.]{1}[a-z]{2,3}))$/ -bad10 = /^(([a-z])+.)+[A-Z]([a-z])+$/ +bad7 = /^([a-z]+)+$/ # $ Alert +bad8 = /^([a-z]*)*$/ # $ Alert +bad9 = /^([a-zA-Z0-9])(([\\.-]|[_]+)?([a-zA-Z0-9]+))*(@){1}[a-z0-9]+[.]{1}(([a-z]{2,3})|([a-z]{2,3}[.]{1}[a-z]{2,3}))$/ # $ Alert +bad10 = /^(([a-z])+.)+[A-Z]([a-z])+$/ # $ Alert # NOT GOOD; attack: "[" + "][".repeat(100) + "]!" # Adapted from Prototype.js (https://github.com/prototypejs/prototype), which # is licensed under the MIT license; see file Prototype.js-LICENSE. -bad11 = /(([\w#:.~>+()\s-]+|\*|\[.*?\])+)\s*(,|$)/ +bad11 = /(([\w#:.~>+()\s-]+|\*|\[.*?\])+)\s*(,|$)/ # $ Alert # NOT GOOD; attack: "'" + "\\a".repeat(100) + '"' # Adapted from Prism (https://github.com/PrismJS/prism), which is licensed # under the MIT license; see file Prism-LICENSE. -bad12 = /("|')(\\?.)*?\1/ +bad12 = /("|')(\\?.)*?\1/ # $ Alert # NOT GOOD -bad13 = /(b|a?b)*c/ +bad13 = /(b|a?b)*c/ # $ Alert # NOT GOOD -bad15 = /(a|aa?)*b/ +bad15 = /(a|aa?)*b/ # $ Alert # GOOD good7 = /(.|\n)*!/ # NOT GOOD; attack: "\n".repeat(100) + "." -bad16 = /(.|\n)*!/m +bad16 = /(.|\n)*!/m # $ Alert # GOOD good8 = /([\w.]+)*/ # NOT GOOD -bad17 = Regexp.new '(a|aa?)*b' +bad17 = Regexp.new '(a|aa?)*b' # $ Alert # GOOD - not used as regexp good9 = '(a|aa?)*b' # NOT GOOD -bad18 = /(([\S\s]|[^a])*)"/ +bad18 = /(([\S\s]|[^a])*)"/ # $ Alert # GOOD - there is no witness in the end that could cause the regexp to not match good10 = /([^"']+)*/ # NOT GOOD -bad20 = /((.|[^a])*)"/ +bad20 = /((.|[^a])*)"/ # $ Alert # GOOD good10 = /((a|[^a])*)"/ # NOT GOOD -bad21 = /((b|[^a])*)"/ +bad21 = /((b|[^a])*)"/ # $ Alert # NOT GOOD -bad22 = /((G|[^a])*)"/ +bad22 = /((G|[^a])*)"/ # $ Alert # NOT GOOD -bad23 = /(([0-9]|[^a])*)"/ +bad23 = /(([0-9]|[^a])*)"/ # $ Alert # BAD - missing result bad24 = /(?:=(?:([!#\$%&'\*\+\-\.\^_`\|~0-9A-Za-z]+)|"((?:\\[\x00-\x7f]|[^\x00-\x08\x0a-\x1f\x7f"])*)"))?/ @@ -122,55 +122,55 @@ bad25 = /"((?:\\[\x00-\x7f]|[^\x00-\x08\x0a-\x1f\x7f"])*)"/ bad26 = /"((?:\\[\x00-\x7f]|[^\x00-\x08\x0a-\x1f\x7f"\\])*)"/ # NOT GOOD -bad27 = /(([a-z]|[d-h])*)"/ +bad27 = /(([a-z]|[d-h])*)"/ # $ Alert # NOT GOOD -bad27 = /(([^a-z]|[^0-9])*)"/ +bad27 = /(([^a-z]|[^0-9])*)"/ # $ Alert # NOT GOOD -bad28 = /((\d|[0-9])*)"/ +bad28 = /((\d|[0-9])*)"/ # $ Alert # NOT GOOD -bad29 = /((\s|\s)*)"/ +bad29 = /((\s|\s)*)"/ # $ Alert # NOT GOOD -bad30 = /((\w|G)*)"/ +bad30 = /((\w|G)*)"/ # $ Alert # GOOD good11 = /((\s|\d)*)"/ # NOT GOOD -bad31 = /((\d|\w)*)"/ +bad31 = /((\d|\w)*)"/ # $ Alert # NOT GOOD -bad32 = /((\d|5)*)"/ +bad32 = /((\d|5)*)"/ # $ Alert # BAD - \f is not handled correctly -bad33 = /((\s|[\f])*)"/ +bad33 = /((\s|[\f])*)"/ # $ Alert # BAD - \v is not handled correctly -bad34 = /((\s|[\v]|\\v)*)"/ +bad34 = /((\s|[\v]|\\v)*)"/ # $ Alert # NOT GOOD -bad35 = /((\f|[\f])*)"/ +bad35 = /((\f|[\f])*)"/ # $ Alert # NOT GOOD -bad36 = /((\W|\D)*)"/ +bad36 = /((\W|\D)*)"/ # $ Alert # NOT GOOD -bad37 = /((\S|\w)*)"/ +bad37 = /((\S|\w)*)"/ # $ Alert # NOT GOOD -bad38 = /((\S|[\w])*)"/ +bad38 = /((\S|[\w])*)"/ # $ Alert # NOT GOOD -bad39 = /((1s|[\da-z])*)"/ +bad39 = /((1s|[\da-z])*)"/ # $ Alert # NOT GOOD -bad40 = /((0|[\d])*)"/ +bad40 = /((0|[\d])*)"/ # $ Alert # NOT GOOD -bad41 = /(([\d]+)*)"/ +bad41 = /(([\d]+)*)"/ # $ Alert # GOOD - there is no witness in the end that could cause the regexp to not match good12 = /(\d+(X\d+)?)+/ @@ -182,49 +182,49 @@ good13 = /([0-9]+(X[0-9]*)?)*/ good15 = /^([^>]+)*(>|$)/ # NOT GOOD -bad43 = /^([^>a]+)*(>|$)/ +bad43 = /^([^>a]+)*(>|$)/ # $ Alert # NOT GOOD -bad44 = /(\n\s*)+$/ +bad44 = /(\n\s*)+$/ # $ Alert # NOT GOOD -bad45 = /^(?:\s+|#.*|\(\?#[^)]*\))*(?:[?*+]|{\d+(?:,\d*)?})/ +bad45 = /^(?:\s+|#.*|\(\?#[^)]*\))*(?:[?*+]|{\d+(?:,\d*)?})/ # $ Alert # NOT GOOD -bad46 = /\{\[\s*([a-zA-Z]+)\(([a-zA-Z]+)\)((\s*([a-zA-Z]+)\: ?([ a-zA-Z{}]+),?)+)*\s*\]\}/ +bad46 = /\{\[\s*([a-zA-Z]+)\(([a-zA-Z]+)\)((\s*([a-zA-Z]+)\: ?([ a-zA-Z{}]+),?)+)*\s*\]\}/ # $ Alert # NOT GOOD -bad47 = /(a+|b+|c+)*c/ +bad47 = /(a+|b+|c+)*c/ # $ Alert # NOT GOOD -bad48 = /(((a+a?)*)+b+)/ +bad48 = /(((a+a?)*)+b+)/ # $ Alert # NOT GOOD -bad49 = /(a+)+bbbb/ +bad49 = /(a+)+bbbb/ # $ Alert # GOOD good16 = /(a+)+aaaaa*a+/ # NOT GOOD -bad50 = /(a+)+aaaaa$/ +bad50 = /(a+)+aaaaa$/ # $ Alert # GOOD good17 = /(\n+)+\n\n/ # NOT GOOD -bad51 = /(\n+)+\n\n$/ +bad51 = /(\n+)+\n\n$/ # $ Alert # NOT GOOD -bad52 = /([^X]+)*$/ +bad52 = /([^X]+)*$/ # $ Alert # NOT GOOD -bad53 = /(([^X]b)+)*$/ +bad53 = /(([^X]b)+)*$/ # $ Alert # GOOD good18 = /(([^X]b)+)*($|[^X]b)/ # NOT GOOD -bad54 = /(([^X]b)+)*($|[^X]c)/ +bad54 = /(([^X]b)+)*($|[^X]c)/ # $ Alert # GOOD good20 = /((ab)+)*ababab/ @@ -236,13 +236,13 @@ good21 = /((ab)+)*abab(ab)*(ab)+/ good22 = /((ab)+)*/ # NOT GOOD -bad55 = /((ab)+)*$/ +bad55 = /((ab)+)*$/ # $ Alert # GOOD good23 = /((ab)+)*[a1][b1][a2][b2][a3][b3]/ # NOT GOOD -bad56 = /([\n\s]+)*(.)/ +bad56 = /([\n\s]+)*(.)/ # $ Alert # GOOD - any witness passes through the accept state. good24 = /(A*A*X)*/ @@ -251,13 +251,13 @@ good24 = /(A*A*X)*/ good26 = /([^\\\]]+)*/ # NOT GOOD -bad59 = /(\w*foobarbaz\w*foobarbaz\w*foobarbaz\w*foobarbaz\s*foobarbaz\d*foobarbaz\w*)+-/ +bad59 = /(\w*foobarbaz\w*foobarbaz\w*foobarbaz\w*foobarbaz\s*foobarbaz\d*foobarbaz\w*)+-/ # $ Alert # NOT GOOD -bad60 = /(.thisisagoddamnlongstringforstresstestingthequery|\sthisisagoddamnlongstringforstresstestingthequery)*-/ +bad60 = /(.thisisagoddamnlongstringforstresstestingthequery|\sthisisagoddamnlongstringforstresstestingthequery)*-/ # $ Alert # NOT GOOD -bad61 = /(thisisagoddamnlongstringforstresstestingthequery|this\w+query)*-/ +bad61 = /(thisisagoddamnlongstringforstresstestingthequery|this\w+query)*-/ # $ Alert # GOOD good27 = /(thisisagoddamnlongstringforstresstestingthequery|imanotherbutunrelatedstringcomparedtotheotherstring)*-/ @@ -269,114 +269,114 @@ good27 = /(thisisagoddamnlongstringforstresstestingthequery|imanotherbutunrelate #good29 = /foo((\uDC66|\uDC67)|(\uDC68|\uDC69))*foo/ # NOT GOOD (but cannot currently construct a prefix) -bad62 = /a{2,3}(b+)+X/ +bad62 = /a{2,3}(b+)+X/ # $ Alert # NOT GOOD (and a good prefix test) -bad63 = /^<(\w+)((?:\s+\w+(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*)\s*(\/?)>/ +bad63 = /^<(\w+)((?:\s+\w+(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*)\s*(\/?)>/ # $ Alert # GOOD good30 = /(a+)*[\S\s][\S\s][\S\s]?/ # GOOD - but we fail to see that repeating the attack string ends in the "accept any" state (due to not parsing the range `[^]{2,3}`). -good31 = /(a+)*[\S\s]{2,3}/ +good31 = /(a+)*[\S\s]{2,3}/ # $ Alert # GOOD - but we spuriously conclude that a rejecting suffix exists (due to not parsing the range `[^]{2,}` when constructing the NFA). -good32 = /(a+)*([\S\s]{2,}|X)$/ +good32 = /(a+)*([\S\s]{2,}|X)$/ # $ Alert # GOOD good33 = /(a+)*([\S\s]*|X)$/ # NOT GOOD -bad64 = /((a+)*$|[\S\s]+)/ +bad64 = /((a+)*$|[\S\s]+)/ # $ Alert # GOOD - but still flagged. The only change compared to the above is the order of alternatives, which we don't model. -good34 = /([\S\s]+|(a+)*$)/ +good34 = /([\S\s]+|(a+)*$)/ # $ Alert # GOOD good35 = /((;|^)a+)+$/ # NOT GOOD (a good prefix test) -bad65 = /(^|;)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(e+)+f/ +bad65 = /(^|;)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(e+)+f/ # $ Alert # NOT GOOD -bad66 = /^ab(c+)+$/ +bad66 = /^ab(c+)+$/ # $ Alert # NOT GOOD -bad67 = /(\d(\s+)*){20}/ +bad67 = /(\d(\s+)*){20}/ # $ Alert -# GOOD - but we spuriously conclude that a rejecting suffix exists. -good36 = /(([^\/]|X)+)(\/[\S\s]*)*$/ +# GOOD - but we spuriously conclude that a rejecting suffix exists. +good36 = /(([^\/]|X)+)(\/[\S\s]*)*$/ # $ Alert -# GOOD - but we spuriously conclude that a rejecting suffix exists. -good37 = /^((x([^Y]+)?)*(Y|$))/ +# GOOD - but we spuriously conclude that a rejecting suffix exists. +good37 = /^((x([^Y]+)?)*(Y|$))/ # $ Alert # NOT GOOD -bad68 = /(a*)+b/ +bad68 = /(a*)+b/ # $ Alert # NOT GOOD -bad69 = /foo([\w-]*)+bar/ +bad69 = /foo([\w-]*)+bar/ # $ Alert # NOT GOOD -bad70 = /((ab)*)+c/ +bad70 = /((ab)*)+c/ # $ Alert # NOT GOOD -bad71 = /(a?a?)*b/ +bad71 = /(a?a?)*b/ # $ Alert # GOOD good38 = /(a?)*b/ # NOT GOOD - but not detected -bad72 = /(c?a?)*b/ +bad72 = /(c?a?)*b/ # $ MISSING: Alert # NOT GOOD -bad73 = /(?:a|a?)+b/ +bad73 = /(?:a|a?)+b/ # $ Alert -# NOT GOOD - but not detected. -bad74 = /(a?b?)*$/ +# NOT GOOD - but not detected. +bad74 = /(a?b?)*$/ # $ MISSING: Alert # NOT GOOD -bad76 = /PRE(([a-c]|[c-d])T(e?e?e?e?|X))+(cTcT|cTXcTX$)/ - -# NOT GOOD - but not detected -bad77 = /^((a)+\w)+$/ +bad76 = /PRE(([a-c]|[c-d])T(e?e?e?e?|X))+(cTcT|cTXcTX$)/ # $ Alert # NOT GOOD -bad78 = /^(b+.)+$/ +bad77 = /^((a)+\w)+$/ # $ Alert + +# NOT GOOD +bad78 = /^(b+.)+$/ # $ Alert # GOOD good39 = /a*b/ # All 4 bad combinations of nested * and + -bad79 = /(a*)*b/ -bad80 = /(a+)*b/ -bad81 = /(a*)+b/ -bad82 = /(a+)+b/ +bad79 = /(a*)*b/ # $ Alert +bad80 = /(a+)*b/ # $ Alert +bad81 = /(a*)+b/ # $ Alert +bad82 = /(a+)+b/ # $ Alert # GOOD good40 = /(a|b)+/ good41 = /(?:[\s;,"'<>(){}|\[\]@=+*]|:(?![\/\\]))+/ # NOT GOOD -bad83 = /^((?:a{|-)|\w\{)+X$/ -bad84 = /^((?:a{0|-)|\w\{\d)+X$/ -bad85 = /^((?:a{0,|-)|\w\{\d,)+X$/ -bad86 = /^((?:a{0,2|-)|\w\{\d,\d)+X$/ +bad83 = /^((?:a{|-)|\w\{)+X$/ # $ Alert +bad84 = /^((?:a{0|-)|\w\{\d)+X$/ # $ Alert +bad85 = /^((?:a{0,|-)|\w\{\d,)+X$/ # $ Alert +bad86 = /^((?:a{0,2|-)|\w\{\d,\d)+X$/ # $ Alert -# NOT GOOD +# NOT GOOD bad87 = /^((?:a{0,2}|-)|\w\{\d,\d\})+X$/ # NOT GOOD -bad88 = /^X(\u0061|a)*Y$/ +bad88 = /^X(\u0061|a)*Y$/ # $ Alert # GOOD good43 = /^X(\u0061|b)+Y$/ # NOT GOOD -bad88 = /X([[:digit:]]|\d)+Y/ +bad88 = /X([[:digit:]]|\d)+Y/ # $ Alert # NOT GOOD -bad89 = /\G(a|\w)*$/ -bad90 = /\b(a|\w)*$/ +bad89 = /\G(a|\w)*$/ # $ Alert +bad90 = /\b(a|\w)*$/ # $ Alert # NOT GOOD; attack: "0".repeat(30) + "!" # Adapated from addressable (https://github.com/sporkmonger/addressable) @@ -387,5 +387,5 @@ module Bad91 var_char_class = ALPHA + DIGIT + '_' var_char = "(?:(?:[#{var_char_class}]|%[a-fA-F0-9][a-fA-F0-9])+)" var = "(?:#{var_char}(?:\\.?#{var_char})*)" - bad91 = /^#{var}$/ + bad91 = /^#{var}$/ # $ Alert end diff --git a/ruby/ql/test/query-tests/security/cwe-1333-polynomial-redos/PolynomialReDoS.qlref b/ruby/ql/test/query-tests/security/cwe-1333-polynomial-redos/PolynomialReDoS.qlref index 5807dc56fa0..28e7aa93906 100644 --- a/ruby/ql/test/query-tests/security/cwe-1333-polynomial-redos/PolynomialReDoS.qlref +++ b/ruby/ql/test/query-tests/security/cwe-1333-polynomial-redos/PolynomialReDoS.qlref @@ -1 +1,2 @@ -queries/security/cwe-1333/PolynomialReDoS.ql +query: queries/security/cwe-1333/PolynomialReDoS.ql +postprocess: utils/test/InlineExpectationsTestQuery.ql diff --git a/ruby/ql/test/query-tests/security/cwe-1333-polynomial-redos/PolynomialReDoS.rb b/ruby/ql/test/query-tests/security/cwe-1333-polynomial-redos/PolynomialReDoS.rb index 2f73209321f..249b686fd33 100644 --- a/ruby/ql/test/query-tests/security/cwe-1333-polynomial-redos/PolynomialReDoS.rb +++ b/ruby/ql/test/query-tests/security/cwe-1333-polynomial-redos/PolynomialReDoS.rb @@ -1,35 +1,35 @@ class FooController < ActionController::Base def some_request_handler # A source for the data-flow query (i.e. a remote flow source) - name = params[:name] + name = params[:name] # $ Source # A vulnerable regex regex = /^\s+|\s+$/ # Various sinks that match the source against the regex - name =~ regex # NOT GOOD - name !~ regex # NOT GOOD - name[regex] # NOT GOOD - name.gsub regex, '' # NOT GOOD - name.index regex # NOT GOOD - name.match regex # NOT GOOD - name.match? regex # NOT GOOD - name.partition regex # NOT GOOD - name.rindex regex # NOT GOOD - name.rpartition regex # NOT GOOD - name.scan regex # NOT GOOD - name.split regex # NOT GOOD - name.sub regex, '' # NOT GOOD - regex.match name # NOT GOOD - regex.match? name # NOT GOOD + name =~ regex # $ Alert // NOT GOOD + name !~ regex # $ Alert // NOT GOOD + name[regex] # $ Alert // NOT GOOD + name.gsub regex, '' # $ Alert // NOT GOOD + name.index regex # $ Alert // NOT GOOD + name.match regex # $ Alert // NOT GOOD + name.match? regex # $ Alert // NOT GOOD + name.partition regex # $ Alert // NOT GOOD + name.rindex regex # $ Alert // NOT GOOD + name.rpartition regex # $ Alert // NOT GOOD + name.scan regex # $ Alert // NOT GOOD + name.split regex # $ Alert // NOT GOOD + name.sub regex, '' # $ Alert // NOT GOOD + regex.match name # $ Alert // NOT GOOD + regex.match? name # $ Alert // NOT GOOD # Destructive variants - a = params[:b] - a.gsub! regex, '' # NOT GOOD - b = params[:a] - b.slice! regex # NOT GOOD - c = params[:c] - c.sub! regex, '' # NOT GOOD + a = params[:b] # $ Source + a.gsub! regex, '' # $ Alert // NOT GOOD + b = params[:a] # $ Source + b.slice! regex # $ Alert // NOT GOOD + c = params[:c] # $ Source + c.sub! regex, '' # $ Alert // NOT GOOD # GOOD - guarded by a string length check if name.length < 1024 @@ -39,19 +39,19 @@ class FooController < ActionController::Base # GOOD - regex does not suffer from polynomial backtracking (regression test) params[:foo] =~ /\A[bc].*\Z/ - case name # NOT GOOD + case name # $ Sink // NOT GOOD when regex puts "foo" - end + end # $ Alert - case name # NOT GOOD + case name # $ Sink // NOT GOOD in /^\s+|\s+$/ then puts "foo" - end + end # $ Alert end def some_other_request_handle - name = params[:name] # source + name = params[:name] # $ Source // source indirect_use_of_reg /^\s+|\s+$/, name @@ -59,22 +59,22 @@ class FooController < ActionController::Base end def indirect_use_of_reg (reg, input) - input.gsub reg, '' # NOT GOOD + input.gsub reg, '' # $ Alert // NOT GOOD end def as_string_indirect (reg_as_string, input) - input.match? reg_as_string, '' # NOT GOOD + input.match? reg_as_string, '' # $ Alert // NOT GOOD end def re_compile_indirect - name = params[:name] # source + name = params[:name] # $ Source // source reg = Regexp.new '^\s+|\s+$' re_compile_indirect_2 reg, name end def re_compile_indirect_2 (reg, input) - input.gsub reg, '' # NOT GOOD + input.gsub reg, '' # $ Alert // NOT GOOD end # See https://github.com/dependabot/dependabot-core/blob/37dc1767fde9b7184020763f4d0c1434f93d11d6/python/lib/dependabot/python/requirement_parser.rb#L6-L25 @@ -100,8 +100,8 @@ class FooController < ActionController::Base MARKER_EXPR = /(#{MARKER_EXPR_ONE}|\(\s*|\s*\)|\s+and\s+|\s+or\s+)+/ def use_marker_expr - name = params[:name] # source + name = params[:name] # $ Source // source - name =~ MARKER_EXPR + name =~ MARKER_EXPR # $ Alert end end diff --git a/ruby/ql/test/query-tests/security/cwe-1333-polynomial-redos/lib/index.rb b/ruby/ql/test/query-tests/security/cwe-1333-polynomial-redos/lib/index.rb index b6bf9570f4d..e24e128fee2 100644 --- a/ruby/ql/test/query-tests/security/cwe-1333-polynomial-redos/lib/index.rb +++ b/ruby/ql/test/query-tests/security/cwe-1333-polynomial-redos/lib/index.rb @@ -1,13 +1,13 @@ module Foo - def bar(x) + def bar(x) # $ Source # Run the /a+$/ regex on the input x. - match = x.match(/a+$/) + match = x.match(/a+$/) # $ Alert end protected - def baz(x) - match = x.match(/a+$/) + def baz(x) # $ Source + match = x.match(/a+$/) # $ Alert - match2 = x.match(/(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)C.*Y$/) + match2 = x.match(/(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)C.*Y$/) # $ Alert end -end \ No newline at end of file +end diff --git a/ruby/ql/test/query-tests/security/cwe-1333-regexp-injection/RegExpInjection.qlref b/ruby/ql/test/query-tests/security/cwe-1333-regexp-injection/RegExpInjection.qlref index 11c9e723026..2623c876bf6 100644 --- a/ruby/ql/test/query-tests/security/cwe-1333-regexp-injection/RegExpInjection.qlref +++ b/ruby/ql/test/query-tests/security/cwe-1333-regexp-injection/RegExpInjection.qlref @@ -1 +1,2 @@ -queries/security/cwe-1333/RegExpInjection.ql +query: queries/security/cwe-1333/RegExpInjection.ql +postprocess: utils/test/InlineExpectationsTestQuery.ql diff --git a/ruby/ql/test/query-tests/security/cwe-1333-regexp-injection/RegExpInjection.rb b/ruby/ql/test/query-tests/security/cwe-1333-regexp-injection/RegExpInjection.rb index aca47e42e60..469c084a75b 100644 --- a/ruby/ql/test/query-tests/security/cwe-1333-regexp-injection/RegExpInjection.rb +++ b/ruby/ql/test/query-tests/security/cwe-1333-regexp-injection/RegExpInjection.rb @@ -1,26 +1,26 @@ class FooController < ActionController::Base # BAD def route0 - name = params[:name] - regex = /#{name}/ + name = params[:name] # $ Source + regex = /#{name}/ # $ Alert end # BAD def route1 - name = params[:name] - regex = /foo#{name}bar/ + name = params[:name] # $ Source + regex = /foo#{name}bar/ # $ Alert end # BAD def route2 - name = params[:name] - regex = Regexp.new(name) + name = params[:name] # $ Source + regex = Regexp.new(name) # $ Alert end # BAD def route3 - name = params[:name] - regex = Regexp.new("@" + name) + name = params[:name] # $ Source + regex = Regexp.new("@" + name) # $ Alert end # GOOD - string is compared against a constant string @@ -51,7 +51,7 @@ class FooController < ActionController::Base # BAD def route8 - name = params[:name] - regex = Regexp.compile("@" + name) + name = params[:name] # $ Source + regex = Regexp.compile("@" + name) # $ Alert end end diff --git a/ruby/ql/test/query-tests/security/cwe-134/TaintedFormatString.qlref b/ruby/ql/test/query-tests/security/cwe-134/TaintedFormatString.qlref index c8e1c80ec40..f688cc3f7e3 100644 --- a/ruby/ql/test/query-tests/security/cwe-134/TaintedFormatString.qlref +++ b/ruby/ql/test/query-tests/security/cwe-134/TaintedFormatString.qlref @@ -1 +1,2 @@ -queries/security/cwe-134/TaintedFormatString.ql +query: queries/security/cwe-134/TaintedFormatString.ql +postprocess: utils/test/InlineExpectationsTestQuery.ql diff --git a/ruby/ql/test/query-tests/security/cwe-134/tainted_format_string.rb b/ruby/ql/test/query-tests/security/cwe-134/tainted_format_string.rb index aa66a9aa470..fb21b61c14f 100644 --- a/ruby/ql/test/query-tests/security/cwe-134/tainted_format_string.rb +++ b/ruby/ql/test/query-tests/security/cwe-134/tainted_format_string.rb @@ -1,44 +1,44 @@ class UsersController < ActionController::Base def show - printf(params[:format], arg) # BAD - Kernel.printf(params[:format], arg) # BAD + printf(params[:format], arg) # $ Alert // BAD + Kernel.printf(params[:format], arg) # $ Alert // BAD printf(params[:format]) # GOOD Kernel.printf(params[:format]) # GOOD - printf(IO.new(1), params[:format], arg) # BAD - Kernel.printf(IO.new(1), params[:format], arg) # BAD + printf(IO.new(1), params[:format], arg) # $ Alert // BAD + Kernel.printf(IO.new(1), params[:format], arg) # $ Alert // BAD printf("%s", params[:format]) # GOOD Kernel.printf("%s", params[:format]) # GOOD fmt = "%s" printf(fmt, params[:format]) # GOOD - printf(IO.new(1), params[:format]) # GOOD [FALSE POSITIVE] - Kernel.printf(IO.new(1), params[:format]) # GOOD [FALSE POSITIVE] + printf(IO.new(1), params[:format]) # $ Alert // GOOD [FALSE POSITIVE] + Kernel.printf(IO.new(1), params[:format]) # $ Alert // GOOD [FALSE POSITIVE] - str1 = Kernel.sprintf(params[:format], arg) # BAD - str2 = sprintf(params[:format], arg) # BAD + str1 = Kernel.sprintf(params[:format], arg) # $ Alert // BAD + str2 = sprintf(params[:format], arg) # $ Alert // BAD str1 = Kernel.sprintf(params[:format]) # GOOD str2 = sprintf(params[:format]) # GOOD stdout = IO.new 1 - stdout.printf(params[:format], arg) # BAD + stdout.printf(params[:format], arg) # $ Alert // BAD stdout.printf(params[:format]) # GOOD # Taint via string concatenation - printf("A log message: " + params[:format], arg) # BAD + printf("A log message: " + params[:format], arg) # $ Alert // BAD # Taint via string interpolation - printf("A log message: #{params[:format]}", arg) # BAD + printf("A log message: #{params[:format]}", arg) # $ Alert // BAD # Using String# - "A log message #{params[:format]} %{foo}" % {foo: "foo"} # BAD + "A log message #{params[:format]} %{foo}" % {foo: "foo"} # $ Alert // BAD # String# with an array - "A log message #{params[:format]} %08x" % ["foo"] # BAD + "A log message #{params[:format]} %08x" % ["foo"] # $ Alert // BAD end -end \ No newline at end of file +end diff --git a/ruby/ql/test/query-tests/security/cwe-209/StackTraceExposure.qlref b/ruby/ql/test/query-tests/security/cwe-209/StackTraceExposure.qlref index c110f2b1765..ebd3ae1cee1 100644 --- a/ruby/ql/test/query-tests/security/cwe-209/StackTraceExposure.qlref +++ b/ruby/ql/test/query-tests/security/cwe-209/StackTraceExposure.qlref @@ -1 +1,2 @@ -queries/security/cwe-209/StackTraceExposure.ql \ No newline at end of file +query: queries/security/cwe-209/StackTraceExposure.ql +postprocess: utils/test/InlineExpectationsTestQuery.ql diff --git a/ruby/ql/test/query-tests/security/cwe-209/StackTraceExposure.rb b/ruby/ql/test/query-tests/security/cwe-209/StackTraceExposure.rb index dcdf5c1f22c..19e0c7972cf 100644 --- a/ruby/ql/test/query-tests/security/cwe-209/StackTraceExposure.rb +++ b/ruby/ql/test/query-tests/security/cwe-209/StackTraceExposure.rb @@ -3,19 +3,19 @@ class FooController < ApplicationController def show something_that_might_fail() rescue => e - render body: e.backtrace, content_type: "text/plain" + render body: e.backtrace, content_type: "text/plain" # $ Alert end def show2 - bt = caller() - render body: bt, content_type: "text/plain" + bt = caller() # $ Source + render body: bt, content_type: "text/plain" # $ Alert end def show3 not_a_method() rescue NoMethodError => e - render body: e.backtrace, content_type: "text/plain" + render body: e.backtrace, content_type: "text/plain" # $ Alert end end diff --git a/ruby/ql/test/query-tests/security/cwe-295/Excon.rb b/ruby/ql/test/query-tests/security/cwe-295/Excon.rb index 8bdabc31cf2..08b754f380c 100644 --- a/ruby/ql/test/query-tests/security/cwe-295/Excon.rb +++ b/ruby/ql/test/query-tests/security/cwe-295/Excon.rb @@ -3,31 +3,31 @@ require "excon" def method1 # BAD Excon.defaults[:ssl_verify_peer] = false - Excon.get("http://example.com/") + Excon.get("http://example.com/") # $ Alert end def method2 # BAD Excon.ssl_verify_peer = false - Excon.get("http://example.com/") + Excon.get("http://example.com/") # $ Alert end def method3(secure) # BAD Excon.defaults[:ssl_verify_peer] = (secure ? true : false) - Excon.get("http://example.com/") + Excon.get("http://example.com/") # $ Alert end def method4 # BAD conn = Excon::Connection.new("http://example.com/", ssl_verify_peer: false) - conn.get + conn.get # $ Alert end def method5 # BAD Excon.ssl_verify_peer = true - Excon.new("http://example.com/", ssl_verify_peer: false).get + Excon.new("http://example.com/", ssl_verify_peer: false).get # $ Alert end def method6 @@ -65,4 +65,4 @@ def method10 # GOOD connection = Excon.new("foo") connection.get("bar") -end \ No newline at end of file +end diff --git a/ruby/ql/test/query-tests/security/cwe-295/Faraday.rb b/ruby/ql/test/query-tests/security/cwe-295/Faraday.rb index 6c12db2c9e6..1e298b82aeb 100644 --- a/ruby/ql/test/query-tests/security/cwe-295/Faraday.rb +++ b/ruby/ql/test/query-tests/security/cwe-295/Faraday.rb @@ -2,11 +2,11 @@ require "faraday" # BAD connection = Faraday.new("http://example.com", ssl: { verify: false }) -response = connection.get("/") +response = connection.get("/") # $ Alert # BAD connection = Faraday.new("http://example.com", ssl: { verify_mode: OpenSSL::SSL::VERIFY_NONE }) -response = connection.get("/") +response = connection.get("/") # $ Alert # GOOD connection = Faraday.new("http://example.com") @@ -32,7 +32,7 @@ response = connection.get("/") def verify_as_arg(host, path, arg) # BAD, due to the call below connection = Faraday.new(host, ssl: { verify: arg }) - response = connection.get(path) + response = connection.get(path) # $ Alert end verify_as_arg("http://example.com", "/", false) @@ -41,7 +41,7 @@ verify_as_arg("http://example.com", "/", false) def verify_mode_as_arg(host, path, arg) # BAD, due to the call below connection = Faraday.new(host, ssl: { verify_mode: arg }) - response = connection.get(path) + response = connection.get(path) # $ Alert end verify_mode_as_arg("http://example.com", "/", OpenSSL::SSL::VERIFY_NONE) diff --git a/ruby/ql/test/query-tests/security/cwe-295/HttpClient.rb b/ruby/ql/test/query-tests/security/cwe-295/HttpClient.rb index 902950e5be9..01a96461a46 100644 --- a/ruby/ql/test/query-tests/security/cwe-295/HttpClient.rb +++ b/ruby/ql/test/query-tests/security/cwe-295/HttpClient.rb @@ -3,7 +3,7 @@ require "httpclient" # BAD client = HTTPClient.new client.ssl_config.verify_mode = OpenSSL::SSL::VERIFY_NONE -client.get("https://example.com") +client.get("https://example.com") # $ Alert # GOOD client = HTTPClient.new @@ -15,4 +15,4 @@ client = HTTPClient.new client.get("https://example.com") # GOOD -HTTPClient.get("https://example.com/") \ No newline at end of file +HTTPClient.get("https://example.com/") diff --git a/ruby/ql/test/query-tests/security/cwe-295/Httparty.rb b/ruby/ql/test/query-tests/security/cwe-295/Httparty.rb index 562cbbc1f43..8030e9e119c 100644 --- a/ruby/ql/test/query-tests/security/cwe-295/Httparty.rb +++ b/ruby/ql/test/query-tests/security/cwe-295/Httparty.rb @@ -1,19 +1,19 @@ require "httparty" # BAD -HTTParty.get("http://example.com/", verify: false) +HTTParty.get("http://example.com/", verify: false) # $ Alert # BAD -HTTParty.get("http://example.com/", verify_peer: false) +HTTParty.get("http://example.com/", verify_peer: false) # $ Alert # BAD -HTTParty.get("http://example.com/", { verify_peer: false }) +HTTParty.get("http://example.com/", { verify_peer: false }) # $ Alert # BAD -HTTParty.post("http://example.com/", body: "some_data", verify: false) +HTTParty.post("http://example.com/", body: "some_data", verify: false) # $ Alert # BAD -HTTParty.post("http://example.com/", { body: "some_data", verify: false }) +HTTParty.post("http://example.com/", { body: "some_data", verify: false }) # $ Alert # GOOD HTTParty.get("http://example.com/") @@ -34,4 +34,4 @@ HTTParty.post("http://example.com/", body: "some_data", verify: true) HTTParty.post("http://example.com/", { body: "some_data" }) # GOOD -HTTParty.post("http://example.com/", { body: "some_data", verify: true }) \ No newline at end of file +HTTParty.post("http://example.com/", { body: "some_data", verify: true }) diff --git a/ruby/ql/test/query-tests/security/cwe-295/NetHttp.rb b/ruby/ql/test/query-tests/security/cwe-295/NetHttp.rb index 9269eeae531..7915e8b80d6 100644 --- a/ruby/ql/test/query-tests/security/cwe-295/NetHttp.rb +++ b/ruby/ql/test/query-tests/security/cwe-295/NetHttp.rb @@ -6,5 +6,5 @@ http = Net::HTTP.new uri.host, uri.port http.use_ssl = true http.verify_mode = OpenSSL::SSL::VERIFY_NONE request = Net::HTTP::Get.new uri.request_uri -response = http.request request +response = http.request request # $ Alert puts response.body diff --git a/ruby/ql/test/query-tests/security/cwe-295/OpenURI.rb b/ruby/ql/test/query-tests/security/cwe-295/OpenURI.rb index a825791c823..ae9698f2f68 100644 --- a/ruby/ql/test/query-tests/security/cwe-295/OpenURI.rb +++ b/ruby/ql/test/query-tests/security/cwe-295/OpenURI.rb @@ -1,24 +1,24 @@ require "open-uri" # BAD -Kernel.open("https://example.com", ssl_verify_mode: OpenSSL::SSL::VERIFY_NONE) +Kernel.open("https://example.com", ssl_verify_mode: OpenSSL::SSL::VERIFY_NONE) # $ Alert # BAD -Kernel.open("https://example.com", { ssl_verify_mode: OpenSSL::SSL::VERIFY_NONE }) +Kernel.open("https://example.com", { ssl_verify_mode: OpenSSL::SSL::VERIFY_NONE }) # $ Alert # BAD options = { ssl_verify_mode: OpenSSL::SSL::VERIFY_NONE } -Kernel.open("https://example.com", options) +Kernel.open("https://example.com", options) # $ Alert # BAD -URI.parse("https://example.com").open(ssl_verify_mode: OpenSSL::SSL::VERIFY_NONE) +URI.parse("https://example.com").open(ssl_verify_mode: OpenSSL::SSL::VERIFY_NONE) # $ Alert # BAD -URI.parse("https://example.com").open({ ssl_verify_mode: OpenSSL::SSL::VERIFY_NONE }) +URI.parse("https://example.com").open({ ssl_verify_mode: OpenSSL::SSL::VERIFY_NONE }) # $ Alert # BAD options = { ssl_verify_mode: OpenSSL::SSL::VERIFY_NONE } -URI.parse("https://example.com").open(options) +URI.parse("https://example.com").open(options) # $ Alert # GOOD Kernel.open("https://example.com") @@ -44,4 +44,4 @@ URI.parse("https://example.com").open({ ssl_verify_mode: OpenSSL::SSL::VERIFY_PE # GOOD options = { ssl_verify_mode: OpenSSL::SSL::VERIFY_PEER } -URI.parse("https://example.com").open(options) \ No newline at end of file +URI.parse("https://example.com").open(options) diff --git a/ruby/ql/test/query-tests/security/cwe-295/RequestWithoutValidation.qlref b/ruby/ql/test/query-tests/security/cwe-295/RequestWithoutValidation.qlref index e2caf232ddb..22b77bdb4b0 100644 --- a/ruby/ql/test/query-tests/security/cwe-295/RequestWithoutValidation.qlref +++ b/ruby/ql/test/query-tests/security/cwe-295/RequestWithoutValidation.qlref @@ -1 +1,2 @@ -queries/security/cwe-295/RequestWithoutValidation.ql \ No newline at end of file +query: queries/security/cwe-295/RequestWithoutValidation.ql +postprocess: utils/test/InlineExpectationsTestQuery.ql diff --git a/ruby/ql/test/query-tests/security/cwe-295/RestClient.rb b/ruby/ql/test/query-tests/security/cwe-295/RestClient.rb index a180ac0d74c..91160728823 100644 --- a/ruby/ql/test/query-tests/security/cwe-295/RestClient.rb +++ b/ruby/ql/test/query-tests/security/cwe-295/RestClient.rb @@ -2,21 +2,21 @@ require "rest-client" # BAD resource = RestClient::Resource.new("https://example.com", verify_ssl: OpenSSL::SSL::VERIFY_NONE) -response = resource.get +response = resource.get # $ Alert # BAD resource = RestClient::Resource.new("https://example.com", { verify_ssl: OpenSSL::SSL::VERIFY_NONE }) -response = resource.get +response = resource.get # $ Alert # BAD options = { verify_ssl: OpenSSL::SSL::VERIFY_NONE } resource = RestClient::Resource.new("https://example.com", options) -response = resource.get +response = resource.get # $ Alert # BAD value = OpenSSL::SSL::VERIFY_NONE resource = RestClient::Resource.new("https://example.com", verify_ssl: value) -response = resource.get +response = resource.get # $ Alert # GOOD RestClient.get("https://example.com") diff --git a/ruby/ql/test/query-tests/security/cwe-295/Typhoeus.rb b/ruby/ql/test/query-tests/security/cwe-295/Typhoeus.rb index aed601cf888..af88218d1bc 100644 --- a/ruby/ql/test/query-tests/security/cwe-295/Typhoeus.rb +++ b/ruby/ql/test/query-tests/security/cwe-295/Typhoeus.rb @@ -1,11 +1,11 @@ require "typhoeus" # BAD -Typhoeus.get("https://www.example.com", ssl_verifypeer: false) +Typhoeus.get("https://www.example.com", ssl_verifypeer: false) # $ Alert # BAD post_options = { body: "some data", ssl_verifypeer: false } -Typhoeus.post("https://www.example.com", post_options) +Typhoeus.post("https://www.example.com", post_options) # $ Alert # GOOD -Typhoeus.get("https://www.example.com") \ No newline at end of file +Typhoeus.get("https://www.example.com") diff --git a/ruby/ql/test/query-tests/security/cwe-312/CleartextLogging.qlref b/ruby/ql/test/query-tests/security/cwe-312/CleartextLogging.qlref index 4a8ed809dfc..eb4d8d767b3 100644 --- a/ruby/ql/test/query-tests/security/cwe-312/CleartextLogging.qlref +++ b/ruby/ql/test/query-tests/security/cwe-312/CleartextLogging.qlref @@ -1 +1,2 @@ -queries/security/cwe-312/CleartextLogging.ql \ No newline at end of file +query: queries/security/cwe-312/CleartextLogging.ql +postprocess: utils/test/InlineExpectationsTestQuery.ql diff --git a/ruby/ql/test/query-tests/security/cwe-312/CleartextStorage.qlref b/ruby/ql/test/query-tests/security/cwe-312/CleartextStorage.qlref index 051d588b701..903a20fe574 100644 --- a/ruby/ql/test/query-tests/security/cwe-312/CleartextStorage.qlref +++ b/ruby/ql/test/query-tests/security/cwe-312/CleartextStorage.qlref @@ -1 +1,2 @@ -queries/security/cwe-312/CleartextStorage.ql \ No newline at end of file +query: queries/security/cwe-312/CleartextStorage.ql +postprocess: utils/test/InlineExpectationsTestQuery.ql diff --git a/ruby/ql/test/query-tests/security/cwe-312/app/controllers/users_controller.rb b/ruby/ql/test/query-tests/security/cwe-312/app/controllers/users_controller.rb index 806b5109665..ae277596cfe 100644 --- a/ruby/ql/test/query-tests/security/cwe-312/app/controllers/users_controller.rb +++ b/ruby/ql/test/query-tests/security/cwe-312/app/controllers/users_controller.rb @@ -1,47 +1,47 @@ class UsersController < ApplicationController def createLikeCall - new_password = "043697b96909e03ca907599d6420555f" + new_password = "043697b96909e03ca907599d6420555f" # $ Source[rb/clear-text-storage-sensitive-data] # BAD: plaintext password stored to database - User.create(name: "U1", password: new_password) + User.create(name: "U1", password: new_password) # $ Alert[rb/clear-text-storage-sensitive-data] # BAD: plaintext password stored to database - User.create({ name: "U1", password: new_password }) + User.create({ name: "U1", password: new_password }) # $ Alert[rb/clear-text-storage-sensitive-data] end def updateLikeClassMethodCall - new_password = "083c9e1da4cc0c2f5480bb4dbe6ff141" + new_password = "083c9e1da4cc0c2f5480bb4dbe6ff141" # $ Source[rb/clear-text-storage-sensitive-data] # BAD: plaintext password stored to database - User.update(1, name: "U1", password: new_password) + User.update(1, name: "U1", password: new_password) # $ Alert[rb/clear-text-storage-sensitive-data] # BAD: plaintext password stored to database - User.update([1, 2], [{name: "U1", password: new_password}, {name: "U2", password: new_password}]) + User.update([1, 2], [{name: "U1", password: new_password}, {name: "U2", password: new_password}]) # $ Alert[rb/clear-text-storage-sensitive-data] end def insertAllLikeCall - new_password = "504d224a806cf8073cd14ef08242d422" + new_password = "504d224a806cf8073cd14ef08242d422" # $ Source[rb/clear-text-storage-sensitive-data] # BAD: plaintext password stored to database - User.insert_all([{name: "U1", password: new_password}, {name: "U2", password: new_password}]) + User.insert_all([{name: "U1", password: new_password}, {name: "U2", password: new_password}]) # $ Alert[rb/clear-text-storage-sensitive-data] end def updateLikeInstanceMethodCall user = User.find(1) - new_password = "7d6ae08394c3f284506dca70f05995f6" + new_password = "7d6ae08394c3f284506dca70f05995f6" # $ Source[rb/clear-text-storage-sensitive-data] # BAD: plaintext password stored to database - user.update(password: new_password) + user.update(password: new_password) # $ Alert[rb/clear-text-storage-sensitive-data] # BAD: plaintext password stored to database - user.update({password: new_password}) + user.update({password: new_password}) # $ Alert[rb/clear-text-storage-sensitive-data] end def updateAttributeCall user = User.find(1) - new_password = "ff295f8648a406c37fbe378377320e4c" + new_password = "ff295f8648a406c37fbe378377320e4c" # $ Source[rb/clear-text-storage-sensitive-data] # BAD: plaintext password stored to database - user.update_attribute("password", new_password) + user.update_attribute("password", new_password) # $ Alert[rb/clear-text-storage-sensitive-data] end def assignAttributeCall user = User.find(1) - new_password = "78ffbec583b546bd073efd898f833184" + new_password = "78ffbec583b546bd073efd898f833184" # $ Source[rb/clear-text-storage-sensitive-data] # BAD: plaintext password assigned to database field - user.password = new_password + user.password = new_password # $ Alert[rb/clear-text-storage-sensitive-data] user.save end @@ -55,13 +55,13 @@ class UsersController < ApplicationController end def fileWrites - new_password = "0157af7c38cbdd24f1616de4e5321861" + new_password = "0157af7c38cbdd24f1616de4e5321861" # $ Source[rb/clear-text-storage-sensitive-data] # BAD: plaintext password stored to disk - IO.write("foo.txt", "password: #{new_password}\n") + IO.write("foo.txt", "password: #{new_password}\n") # $ Alert[rb/clear-text-storage-sensitive-data] # BAD: plaintext password stored to disk - File.new("bar.txt", "a").puts("password: #{new_password}") + File.new("bar.txt", "a").puts("password: #{new_password}") # $ Alert[rb/clear-text-storage-sensitive-data] end def randomPasswordAssign @@ -76,15 +76,15 @@ class UsersController < ApplicationController info = [ { name: "U1", - password: "aaaaaaaaaa", - credit_card_number: "0000-0000-0000-0000", - SSN: "000-00-00000" + password: "aaaaaaaaaa", # $ Source[rb/clear-text-storage-sensitive-data] + credit_card_number: "0000-0000-0000-0000", # $ Source[rb/clear-text-storage-sensitive-data] + SSN: "000-00-00000" # $ Source[rb/clear-text-storage-sensitive-data] }, - {name: "U2", password: "bbbbbbb"} + {name: "U2", password: "bbbbbbb"} # $ Source[rb/clear-text-storage-sensitive-data] ] info.each do |inf| # BAD: Plaintext password, SSN, and CCN stored to database. - User.create!(inf) + User.create!(inf) # $ Alert[rb/clear-text-storage-sensitive-data] end end end diff --git a/ruby/ql/test/query-tests/security/cwe-312/app/models/user.rb b/ruby/ql/test/query-tests/security/cwe-312/app/models/user.rb index 09d1866424a..7b5943e641c 100644 --- a/ruby/ql/test/query-tests/security/cwe-312/app/models/user.rb +++ b/ruby/ql/test/query-tests/security/cwe-312/app/models/user.rb @@ -1,20 +1,20 @@ class User < ActiveRecord::Base def set_password_1 - new_password = "06c38c6a8a9c11a9d3b209a3193047b4" + new_password = "06c38c6a8a9c11a9d3b209a3193047b4" # $ Source[rb/clear-text-storage-sensitive-data] # BAD: directly storing a potential cleartext password to a field - self.update(password: new_password) + self.update(password: new_password) # $ Alert[rb/clear-text-storage-sensitive-data] end def set_password_2 - new_password = "52652fb5c709fb6b9b5a0194af7c6067" + new_password = "52652fb5c709fb6b9b5a0194af7c6067" # $ Source[rb/clear-text-storage-sensitive-data] # BAD: directly storing a potential cleartext password to a field - update(password: new_password) + update(password: new_password) # $ Alert[rb/clear-text-storage-sensitive-data] end def set_password_3 - new_password = "f982bf2531c149a8a1444a951b12e830" + new_password = "f982bf2531c149a8a1444a951b12e830" # $ Source[rb/clear-text-storage-sensitive-data] # BAD: directly assigning a potential cleartext password to a field - self.password = new_password + self.password = new_password # $ Alert[rb/clear-text-storage-sensitive-data] self.save end end diff --git a/ruby/ql/test/query-tests/security/cwe-312/logging.rb b/ruby/ql/test/query-tests/security/cwe-312/logging.rb index 26b148f33c2..03b21b3625c 100644 --- a/ruby/ql/test/query-tests/security/cwe-312/logging.rb +++ b/ruby/ql/test/query-tests/security/cwe-312/logging.rb @@ -1,45 +1,45 @@ stdout_logger = Logger.new STDOUT -password = "043697b96909e03ca907599d6420555f" +password = "043697b96909e03ca907599d6420555f" # $ Source[rb/clear-text-logging-sensitive-data] # BAD: password logged as plaintext -stdout_logger.info password +stdout_logger.info password # $ Alert[rb/clear-text-logging-sensitive-data] # BAD: password logged as plaintext -stdout_logger.debug password +stdout_logger.debug password # $ Alert[rb/clear-text-logging-sensitive-data] # BAD: password logged as plaintext -stdout_logger.error password +stdout_logger.error password # $ Alert[rb/clear-text-logging-sensitive-data] # BAD: password logged as plaintext -stdout_logger.fatal password +stdout_logger.fatal password # $ Alert[rb/clear-text-logging-sensitive-data] # BAD: password logged as plaintext -stdout_logger.unknown password +stdout_logger.unknown password # $ Alert[rb/clear-text-logging-sensitive-data] # BAD: password logged as plaintext -stdout_logger.warn password +stdout_logger.warn password # $ Alert[rb/clear-text-logging-sensitive-data] # BAD: password logged as plaintext -stdout_logger.add Logger::WARN, password +stdout_logger.add Logger::WARN, password # $ Alert[rb/clear-text-logging-sensitive-data] # BAD: password logged as plaintext -stdout_logger.add Logger::WARN, "message", password +stdout_logger.add Logger::WARN, "message", password # $ Alert[rb/clear-text-logging-sensitive-data] # BAD: password logged as plaintext -stdout_logger.log Logger::WARN, password +stdout_logger.log Logger::WARN, password # $ Alert[rb/clear-text-logging-sensitive-data] # BAD: password logged as plaintext -stdout_logger << "pw: #{password}" +stdout_logger << "pw: #{password}" # $ Alert[rb/clear-text-logging-sensitive-data] # BAD: sensitive data in the progname will taint subsequent logging calls -stdout_logger.progname = password +stdout_logger.progname = password # $ Alert[rb/clear-text-logging-sensitive-data] -hsh1 = { password: "aec5058e61f7f122998b1a30ee2c66b6" } +hsh1 = { password: "aec5058e61f7f122998b1a30ee2c66b6" } # $ Source[rb/clear-text-logging-sensitive-data] hsh2 = {} # GOOD: no backwards flow stdout_logger.info hsh2[:password] -hsh2[:password] = "beeda625d7306b45784d91ea0336e201" +hsh2[:password] = "beeda625d7306b45784d91ea0336e201" # $ Source[rb/clear-text-logging-sensitive-data] hsh3 = hsh2 # BAD: password logged as plaintext -stdout_logger.info hsh1[:password] +stdout_logger.info hsh1[:password] # $ Alert[rb/clear-text-logging-sensitive-data] # BAD: password logged as plaintext -stdout_logger.info hsh2[:password] +stdout_logger.info hsh2[:password] # $ Alert[rb/clear-text-logging-sensitive-data] # BAD: password logged as plaintext -stdout_logger.info hsh3[:password] +stdout_logger.info hsh3[:password] # $ Alert[rb/clear-text-logging-sensitive-data] # GOOD: not a password stdout_logger.info hsh1[:foo] @@ -61,30 +61,30 @@ stdout_logger.info password_masked_sub_ex # GOOD: password is effectively masked before logging stdout_logger.info password_masked_gsub_ex -password_masked_ineffective_sub = "ca497451f5e883662fb1a37bc9ec7838" -password_masked_ineffective_sub_ex = "ca497451f5e883662fb1a37bc9ec7838" -password_masked_ineffective_gsub = "a7e3747b19930d4f4b8181047194832f" -password_masked_ineffective_gsub_ex = "a7e3747b19930d4f4b8181047194832f" -password_masked_ineffective_sub = password_masked_ineffective_sub.sub(/./, "[password]") +password_masked_ineffective_sub = "ca497451f5e883662fb1a37bc9ec7838" # $ Source[rb/clear-text-logging-sensitive-data] +password_masked_ineffective_sub_ex = "ca497451f5e883662fb1a37bc9ec7838" # $ Source[rb/clear-text-logging-sensitive-data] +password_masked_ineffective_gsub = "a7e3747b19930d4f4b8181047194832f" # $ Source[rb/clear-text-logging-sensitive-data] +password_masked_ineffective_gsub_ex = "a7e3747b19930d4f4b8181047194832f" # $ Source[rb/clear-text-logging-sensitive-data] +password_masked_ineffective_sub = password_masked_ineffective_sub.sub(/./, "[password]") # $ Source[rb/clear-text-logging-sensitive-data] password_masked_ineffective_sub_ex.sub!(/./, "[password]") -password_masked_ineffective_gsub = password_masked_ineffective_gsub.gsub(/[A-Z]/, "*") +password_masked_ineffective_gsub = password_masked_ineffective_gsub.gsub(/[A-Z]/, "*") # $ Source[rb/clear-text-logging-sensitive-data] password_masked_ineffective_gsub_ex.gsub!(/[A-Z]/, "*") # BAD: password masked ineffectively -stdout_logger.info password_masked_ineffective_sub +stdout_logger.info password_masked_ineffective_sub # $ Alert[rb/clear-text-logging-sensitive-data] # BAD: password masked ineffectively -stdout_logger.info password_masked_ineffective_gsub +stdout_logger.info password_masked_ineffective_gsub # $ Alert[rb/clear-text-logging-sensitive-data] # BAD: password masked ineffectively -stdout_logger.info password_masked_ineffective_sub_ex +stdout_logger.info password_masked_ineffective_sub_ex # $ Alert[rb/clear-text-logging-sensitive-data] # BAD: password masked ineffectively -stdout_logger.info password_masked_ineffective_gsub_ex +stdout_logger.info password_masked_ineffective_gsub_ex # $ Alert[rb/clear-text-logging-sensitive-data] def foo(password, logger) # BAD: password logged as plaintext - logger.info password + logger.info password # $ Alert[rb/clear-text-logging-sensitive-data] end -password_arg = "65f2950df2f0e2c38d7ba2ccca767291" +password_arg = "65f2950df2f0e2c38d7ba2ccca767291" # $ Source[rb/clear-text-logging-sensitive-data] foo(password_arg, stdout_logger) foo("65f2950df2f0e2c38d7ba2ccca767292", stdout_logger) diff --git a/ruby/ql/test/query-tests/security/cwe-327/BrokenCryptoAlgorithm.qlref b/ruby/ql/test/query-tests/security/cwe-327/BrokenCryptoAlgorithm.qlref index e1c31fb2d58..92b721c8549 100644 --- a/ruby/ql/test/query-tests/security/cwe-327/BrokenCryptoAlgorithm.qlref +++ b/ruby/ql/test/query-tests/security/cwe-327/BrokenCryptoAlgorithm.qlref @@ -1 +1,2 @@ -queries/security/cwe-327/BrokenCryptoAlgorithm.ql \ No newline at end of file +query: queries/security/cwe-327/BrokenCryptoAlgorithm.ql +postprocess: utils/test/InlineExpectationsTestQuery.ql diff --git a/ruby/ql/test/query-tests/security/cwe-327/WeakSensitiveDataHashing.qlref b/ruby/ql/test/query-tests/security/cwe-327/WeakSensitiveDataHashing.qlref index dcb5a4e62a7..b4891bf7bca 100644 --- a/ruby/ql/test/query-tests/security/cwe-327/WeakSensitiveDataHashing.qlref +++ b/ruby/ql/test/query-tests/security/cwe-327/WeakSensitiveDataHashing.qlref @@ -1 +1,2 @@ -queries/security/cwe-327/WeakSensitiveDataHashing.ql \ No newline at end of file +query: queries/security/cwe-327/WeakSensitiveDataHashing.ql +postprocess: utils/test/InlineExpectationsTestQuery.ql diff --git a/ruby/ql/test/query-tests/security/cwe-327/broken_crypto.rb b/ruby/ql/test/query-tests/security/cwe-327/broken_crypto.rb index 69dcd6b472b..84997f6a2d4 100644 --- a/ruby/ql/test/query-tests/security/cwe-327/broken_crypto.rb +++ b/ruby/ql/test/query-tests/security/cwe-327/broken_crypto.rb @@ -1,19 +1,19 @@ require 'openssl' # BAD: creating a cipher using a weak scheme -weak = OpenSSL::Cipher.new('des3') +weak = OpenSSL::Cipher.new('des3') # $ Alert[rb/weak-cryptographic-algorithm] weak.encrypt weak.random_key # BAD: encrypting data using a weak cipher -weak.update('foo') +weak.update('foo') # $ Alert[rb/weak-cryptographic-algorithm] weak.final # BAD: creating a cipher using a weak block mode -weak = OpenSSL::Cipher::AES.new(128, 'ecb') +weak = OpenSSL::Cipher::AES.new(128, 'ecb') # $ Alert[rb/weak-cryptographic-algorithm] weak.encrypt weak.random_key # BAD: encrypting data using a weak block mode -weak.update('foo') +weak.update('foo') # $ Alert[rb/weak-cryptographic-algorithm] weak.final # GOOD: creating a cipher using a strong scheme @@ -25,7 +25,7 @@ strong.update('bar') strong.final # BAD: weak block mode -OpenSSL::Cipher::AES.new(128, :ecb) +OpenSSL::Cipher::AES.new(128, :ecb) # $ Alert[rb/weak-cryptographic-algorithm] # GOOD: strong encryption algorithm OpenSSL::Cipher::AES.new(128, 'cbc') # GOOD: strong encryption algorithm @@ -34,49 +34,49 @@ OpenSSL::Cipher::AES.new('128-cbc') # GOOD: strong encryption algorithm OpenSSL::Cipher::AES128.new # BAD: weak block mode -OpenSSL::Cipher::AES128.new 'ecb' +OpenSSL::Cipher::AES128.new 'ecb' # $ Alert[rb/weak-cryptographic-algorithm] # GOOD: strong encryption algorithm OpenSSL::Cipher::AES192.new # BAD: weak block mode -OpenSSL::Cipher::AES192.new 'ecb' +OpenSSL::Cipher::AES192.new 'ecb' # $ Alert[rb/weak-cryptographic-algorithm] # GOOD: strong encryption algorithm OpenSSL::Cipher::AES256.new # BAD: weak block mode -OpenSSL::Cipher::AES256.new 'ecb' +OpenSSL::Cipher::AES256.new 'ecb' # $ Alert[rb/weak-cryptographic-algorithm] # GOOD: strong encryption algorithm OpenSSL::Cipher::BF.new # BAD: weak block mode -OpenSSL::Cipher::BF.new 'ecb' +OpenSSL::Cipher::BF.new 'ecb' # $ Alert[rb/weak-cryptographic-algorithm] # GOOD: strong encryption algorithm OpenSSL::Cipher::CAST5.new # BAD: weak block mode -OpenSSL::Cipher::CAST5.new 'ecb' +OpenSSL::Cipher::CAST5.new 'ecb' # $ Alert[rb/weak-cryptographic-algorithm] # BAD: weak encryption algorithm -OpenSSL::Cipher::DES.new +OpenSSL::Cipher::DES.new # $ Alert[rb/weak-cryptographic-algorithm] # BAD: weak encryption algorithm -OpenSSL::Cipher::DES.new 'cbc' +OpenSSL::Cipher::DES.new 'cbc' # $ Alert[rb/weak-cryptographic-algorithm] # GOOD: strong encryption algorithm OpenSSL::Cipher::IDEA.new # BAD: weak block mode -OpenSSL::Cipher::IDEA.new 'ecb' +OpenSSL::Cipher::IDEA.new 'ecb' # $ Alert[rb/weak-cryptographic-algorithm] # BAD: weak encryption algorithm -OpenSSL::Cipher::RC2.new +OpenSSL::Cipher::RC2.new # $ Alert[rb/weak-cryptographic-algorithm] # BAD: weak encryption algorithm -OpenSSL::Cipher::RC2.new 'ecb' +OpenSSL::Cipher::RC2.new 'ecb' # $ Alert[rb/weak-cryptographic-algorithm] # BAD: weak encryption algorithm -OpenSSL::Cipher::RC4.new +OpenSSL::Cipher::RC4.new # $ Alert[rb/weak-cryptographic-algorithm] # BAD: weak encryption algorithm -OpenSSL::Cipher::RC4.new '40' +OpenSSL::Cipher::RC4.new '40' # $ Alert[rb/weak-cryptographic-algorithm] # BAD: weak encryption algorithm -OpenSSL::Cipher::RC4.new 'hmac-md5' +OpenSSL::Cipher::RC4.new 'hmac-md5' # $ Alert[rb/weak-cryptographic-algorithm] Digest::MD5.hexdigest('foo') # OK: don't report hash algorithm even if it is weak Digest::SHA256.hexdigest('foo') # GOOD: strong hash algorithm @@ -104,4 +104,4 @@ sha1.digest 'message' # OK: don't report hash algorithm even if it is weak sha1 << 'message' # << is an alias for update OpenSSL::Digest.digest('SHA1', "abc") # OK: don't report hash algorithm even if it is weak -OpenSSL::Digest.digest('SHA3-512', "abc") # GOOD: strong hash algorithm \ No newline at end of file +OpenSSL::Digest.digest('SHA3-512', "abc") # GOOD: strong hash algorithm diff --git a/ruby/ql/test/query-tests/security/cwe-327/weak_hashing.rb b/ruby/ql/test/query-tests/security/cwe-327/weak_hashing.rb index cff4263c40d..13295950b0b 100644 --- a/ruby/ql/test/query-tests/security/cwe-327/weak_hashing.rb +++ b/ruby/ql/test/query-tests/security/cwe-327/weak_hashing.rb @@ -1,16 +1,16 @@ require 'openssl' -password = "abcde" -username = "some_user" +password = "abcde" # $ Source[rb/weak-sensitive-data-hashing] +username = "some_user" # $ Source[rb/weak-sensitive-data-hashing] some_data = "foo" x = password Digest::MD5.hexdigest(some_data) # OK: input is not sensitive Digest::SHA256.hexdigest(password) # OK: strong hash algorithm -Digest::MD5.hexdigest(password) # BAD: weak hash function used for sensitive data -OpenSSL::Digest.digest('SHA1', password) # BAD: weak hash function used for sensitive data -Digest::MD5.hexdigest(username) # BAD: weak hash function used for sensitive data -Digest::MD5.hexdigest(x) # BAD: weak hash function used for sensitive data +Digest::MD5.hexdigest(password) # $ Alert[rb/weak-sensitive-data-hashing] // BAD: weak hash function used for sensitive data +OpenSSL::Digest.digest('SHA1', password) # $ Alert[rb/weak-sensitive-data-hashing] // BAD: weak hash function used for sensitive data +Digest::MD5.hexdigest(username) # $ Alert[rb/weak-sensitive-data-hashing] // BAD: weak hash function used for sensitive data +Digest::MD5.hexdigest(x) # $ Alert[rb/weak-sensitive-data-hashing] // BAD: weak hash function used for sensitive data def get_safe_data() return "hello" @@ -21,13 +21,13 @@ def get_password() end Digest::MD5.hexdigest(get_safe_data()) # OK: input is not sensitive -Digest::MD5.hexdigest(get_password()) # BAD: weak hash function used for sensitive data +Digest::MD5.hexdigest(get_password()) # $ Alert[rb/weak-sensitive-data-hashing] // BAD: weak hash function used for sensitive data some_hash = {password: "changeme", foo: "bar"} Digest::MD5.hexdigest(some_hash[:foo]) # OK: input is not sensitive -Digest::MD5.hexdigest(some_hash[:password]) # BAD: weak hash function used for sensitive data +Digest::MD5.hexdigest(some_hash[:password]) # $ Alert[rb/weak-sensitive-data-hashing] // BAD: weak hash function used for sensitive data -def a_method(safe_data, password_param) +def a_method(safe_data, password_param) # $ Source[rb/weak-sensitive-data-hashing] Digest::MD5.hexdigest(safe_data) # OK: input is not sensitive - Digest::MD5.hexdigest(password_param) # BAD: weak hash function used for sensitive data + Digest::MD5.hexdigest(password_param) # $ Alert[rb/weak-sensitive-data-hashing] // BAD: weak hash function used for sensitive data end diff --git a/ruby/ql/test/query-tests/security/cwe-352/CSRFProtectionDisabled.qlref b/ruby/ql/test/query-tests/security/cwe-352/CSRFProtectionDisabled.qlref index 5dc5050b63e..7e422be7bf5 100644 --- a/ruby/ql/test/query-tests/security/cwe-352/CSRFProtectionDisabled.qlref +++ b/ruby/ql/test/query-tests/security/cwe-352/CSRFProtectionDisabled.qlref @@ -1 +1,2 @@ -queries/security/cwe-352/CSRFProtectionDisabled.ql \ No newline at end of file +query: queries/security/cwe-352/CSRFProtectionDisabled.ql +postprocess: utils/test/InlineExpectationsTestQuery.ql diff --git a/ruby/ql/test/query-tests/security/cwe-352/CSRFProtectionNotEnabled.qlref b/ruby/ql/test/query-tests/security/cwe-352/CSRFProtectionNotEnabled.qlref index 8e9e894fe51..a47a9b3e99a 100644 --- a/ruby/ql/test/query-tests/security/cwe-352/CSRFProtectionNotEnabled.qlref +++ b/ruby/ql/test/query-tests/security/cwe-352/CSRFProtectionNotEnabled.qlref @@ -1 +1,2 @@ -queries/security/cwe-352/CSRFProtectionNotEnabled.ql \ No newline at end of file +query: queries/security/cwe-352/CSRFProtectionNotEnabled.ql +postprocess: utils/test/InlineExpectationsTestQuery.ql diff --git a/ruby/ql/test/query-tests/security/cwe-352/railsapp/app/controllers/alternative_root_controller.rb b/ruby/ql/test/query-tests/security/cwe-352/railsapp/app/controllers/alternative_root_controller.rb index 8cbf31529c1..d6e9df8d22c 100644 --- a/ruby/ql/test/query-tests/security/cwe-352/railsapp/app/controllers/alternative_root_controller.rb +++ b/ruby/ql/test/query-tests/security/cwe-352/railsapp/app/controllers/alternative_root_controller.rb @@ -1,3 +1,3 @@ class AlternativeRootController < ActionController::Base # BAD: no protect_from_forgery call -end \ No newline at end of file +end # $ Alert[rb/csrf-protection-not-enabled] diff --git a/ruby/ql/test/query-tests/security/cwe-352/railsapp/app/controllers/application_controller.rb b/ruby/ql/test/query-tests/security/cwe-352/railsapp/app/controllers/application_controller.rb index 6ff599938e8..0d98c535a41 100644 --- a/ruby/ql/test/query-tests/security/cwe-352/railsapp/app/controllers/application_controller.rb +++ b/ruby/ql/test/query-tests/security/cwe-352/railsapp/app/controllers/application_controller.rb @@ -2,7 +2,7 @@ class ApplicationController < ActionController::Base # BAD: `protect_from_forgery` without `with: :exception` can expose an # application to CSRF attacks in some circumstances - protect_from_forgery + protect_from_forgery # $ Alert[rb/csrf-protection-disabled] before_action authz_guard diff --git a/ruby/ql/test/query-tests/security/cwe-352/railsapp/app/controllers/users_controller.rb b/ruby/ql/test/query-tests/security/cwe-352/railsapp/app/controllers/users_controller.rb index 596a7b0108f..1b54c332cd2 100644 --- a/ruby/ql/test/query-tests/security/cwe-352/railsapp/app/controllers/users_controller.rb +++ b/ruby/ql/test/query-tests/security/cwe-352/railsapp/app/controllers/users_controller.rb @@ -1,7 +1,7 @@ class UsersController < ApplicationController # BAD: Disabling forgery protection may open the application to CSRF attacks - skip_before_action :verify_authenticity_token + skip_before_action :verify_authenticity_token # $ Alert[rb/csrf-protection-disabled] def change_email user = current_user diff --git a/ruby/ql/test/query-tests/security/cwe-352/railsapp/config/application.rb b/ruby/ql/test/query-tests/security/cwe-352/railsapp/config/application.rb index 02b349a1630..5d455ebe347 100644 --- a/ruby/ql/test/query-tests/security/cwe-352/railsapp/config/application.rb +++ b/ruby/ql/test/query-tests/security/cwe-352/railsapp/config/application.rb @@ -13,6 +13,6 @@ module Railsapp config.load_defaults 5.1 # BAD: Disabling forgery protection may open the application to CSRF attacks - config.action_controller.allow_forgery_protection = false + config.action_controller.allow_forgery_protection = false # $ Alert[rb/csrf-protection-disabled] end end diff --git a/ruby/ql/test/query-tests/security/cwe-352/railsapp/config/environments/development.rb b/ruby/ql/test/query-tests/security/cwe-352/railsapp/config/environments/development.rb index a61bc6382b6..968227d5e33 100644 --- a/ruby/ql/test/query-tests/security/cwe-352/railsapp/config/environments/development.rb +++ b/ruby/ql/test/query-tests/security/cwe-352/railsapp/config/environments/development.rb @@ -2,5 +2,5 @@ Rails.application.configure do # Settings specified here will take precedence over those in config/application.rb. # GOOD: disabling CSRF protection in the development environment should not be flagged - config.action_controller.allow_forgery_protection = false + config.action_controller.allow_forgery_protection = false # $ Alert[rb/csrf-protection-disabled] end diff --git a/ruby/ql/test/query-tests/security/cwe-352/railsapp/config/environments/production.rb b/ruby/ql/test/query-tests/security/cwe-352/railsapp/config/environments/production.rb index 1a80e8503a6..384097fccf0 100644 --- a/ruby/ql/test/query-tests/security/cwe-352/railsapp/config/environments/production.rb +++ b/ruby/ql/test/query-tests/security/cwe-352/railsapp/config/environments/production.rb @@ -2,5 +2,5 @@ Rails.application.configure do # Settings specified here will take precedence over those in config/application.rb. # BAD: Disabling forgery protection may open the application to CSRF attacks - config.action_controller.allow_forgery_protection = false + config.action_controller.allow_forgery_protection = false # $ Alert[rb/csrf-protection-disabled] end diff --git a/ruby/ql/test/query-tests/security/cwe-502/oj-global-options/OjGlobalOptions.rb b/ruby/ql/test/query-tests/security/cwe-502/oj-global-options/OjGlobalOptions.rb index 3ec21d778c1..ffaa4107231 100644 --- a/ruby/ql/test/query-tests/security/cwe-502/oj-global-options/OjGlobalOptions.rb +++ b/ruby/ql/test/query-tests/security/cwe-502/oj-global-options/OjGlobalOptions.rb @@ -10,7 +10,7 @@ class UsersController < ActionController::Base # BAD - the safe mode set globally is overridden with an unsafe mode passed as # a call argument def route1 - json_data = params[:key] - object = Oj.load json_data, mode: :object + json_data = params[:key] # $ Source + object = Oj.load json_data, mode: :object # $ Alert end end diff --git a/ruby/ql/test/query-tests/security/cwe-502/oj-global-options/UnsafeDeserialization.qlref b/ruby/ql/test/query-tests/security/cwe-502/oj-global-options/UnsafeDeserialization.qlref index 55f7c440b46..12e3c7a9b6c 100644 --- a/ruby/ql/test/query-tests/security/cwe-502/oj-global-options/UnsafeDeserialization.qlref +++ b/ruby/ql/test/query-tests/security/cwe-502/oj-global-options/UnsafeDeserialization.qlref @@ -1 +1,2 @@ -queries/security/cwe-502/UnsafeDeserialization.ql +query: queries/security/cwe-502/UnsafeDeserialization.ql +postprocess: utils/test/InlineExpectationsTestQuery.ql diff --git a/ruby/ql/test/query-tests/security/cwe-502/ox-global-options/OxGlobalOptions.rb b/ruby/ql/test/query-tests/security/cwe-502/ox-global-options/OxGlobalOptions.rb index 02adc167dab..d43d9cb9173 100644 --- a/ruby/ql/test/query-tests/security/cwe-502/ox-global-options/OxGlobalOptions.rb +++ b/ruby/ql/test/query-tests/security/cwe-502/ox-global-options/OxGlobalOptions.rb @@ -3,8 +3,8 @@ require "ox" class UsersController < ActionController::Base # BAD - Ox.load is unsafe when the mode :object is set globally def route0 - xml_data = params[:key] - object = Ox.load xml_data + xml_data = params[:key] # $ Source + object = Ox.load xml_data # $ Alert end # GOOD - the unsafe mode set globally is overridden with an insecure mode passed as diff --git a/ruby/ql/test/query-tests/security/cwe-502/ox-global-options/UnsafeDeserialization.qlref b/ruby/ql/test/query-tests/security/cwe-502/ox-global-options/UnsafeDeserialization.qlref index 55f7c440b46..12e3c7a9b6c 100644 --- a/ruby/ql/test/query-tests/security/cwe-502/ox-global-options/UnsafeDeserialization.qlref +++ b/ruby/ql/test/query-tests/security/cwe-502/ox-global-options/UnsafeDeserialization.qlref @@ -1 +1,2 @@ -queries/security/cwe-502/UnsafeDeserialization.ql +query: queries/security/cwe-502/UnsafeDeserialization.ql +postprocess: utils/test/InlineExpectationsTestQuery.ql diff --git a/ruby/ql/test/query-tests/security/cwe-502/unsafe-deserialization/UnsafeDeserialization.qlref b/ruby/ql/test/query-tests/security/cwe-502/unsafe-deserialization/UnsafeDeserialization.qlref index 55f7c440b46..12e3c7a9b6c 100644 --- a/ruby/ql/test/query-tests/security/cwe-502/unsafe-deserialization/UnsafeDeserialization.qlref +++ b/ruby/ql/test/query-tests/security/cwe-502/unsafe-deserialization/UnsafeDeserialization.qlref @@ -1 +1,2 @@ -queries/security/cwe-502/UnsafeDeserialization.ql +query: queries/security/cwe-502/UnsafeDeserialization.ql +postprocess: utils/test/InlineExpectationsTestQuery.ql diff --git a/ruby/ql/test/query-tests/security/cwe-502/unsafe-deserialization/UnsafeDeserialization.rb b/ruby/ql/test/query-tests/security/cwe-502/unsafe-deserialization/UnsafeDeserialization.rb index 633a99c14fb..379d6a5819b 100644 --- a/ruby/ql/test/query-tests/security/cwe-502/unsafe-deserialization/UnsafeDeserialization.rb +++ b/ruby/ql/test/query-tests/security/cwe-502/unsafe-deserialization/UnsafeDeserialization.rb @@ -8,26 +8,26 @@ require "yaml" class UsersController < ActionController::Base # BAD def route0 - serialized_data = Base64.decode64 params[:key] - object = Marshal.load serialized_data + serialized_data = Base64.decode64 params[:key] # $ Source + object = Marshal.load serialized_data # $ Alert end # BAD def route1 - serialized_data = Base64.decode64 params[:key] - object = Marshal.restore serialized_data + serialized_data = Base64.decode64 params[:key] # $ Source + object = Marshal.restore serialized_data # $ Alert end # BAD def route2 - json_data = params[:key] - object = JSON.load json_data + json_data = params[:key] # $ Source + object = JSON.load json_data # $ Alert end # BAD def route3 - json_data = params[:key] - object = JSON.restore json_data + json_data = params[:key] # $ Source + object = JSON.restore json_data # $ Alert end # GOOD - JSON.parse is safe to use on untrusted data @@ -38,8 +38,8 @@ class UsersController < ActionController::Base # BAD def route5 - yaml_data = params[:key] - object = YAML.load yaml_data + yaml_data = params[:key] # $ Source + object = YAML.load yaml_data # $ Alert end # GOOD @@ -50,14 +50,14 @@ class UsersController < ActionController::Base # BAD - Oj.load is unsafe in its default :object mode def route7 - json_data = params[:key] - object = Oj.load json_data - object = Oj.load json_data, mode: :object + json_data = params[:key] # $ Source + object = Oj.load json_data # $ Alert + object = Oj.load json_data, mode: :object # $ Alert end # GOOD - Oj.load is safe in any other mode def route8 - json_data = params[:key] + json_data = params[:key] # $ Source # Test the different ways the options hash can be passed options = { allow_blank: true, mode: :rails } object1 = Oj.load json_data, options @@ -67,7 +67,7 @@ class UsersController < ActionController::Base # TODO: false positive; we aren't detecting flow from `:json` to the call argument. more_options = { allow_blank: true } more_options[:mode] = :json - object4 = Oj.load json_data, more_options + object4 = Oj.load json_data, more_options # $ SPURIOUS: Alert end # GOOD @@ -78,20 +78,20 @@ class UsersController < ActionController::Base # BAD - Oj.object_load is always unsafe def route10 - json_data = params[:key] - object = Oj.object_load json_data + json_data = params[:key] # $ Source + object = Oj.object_load json_data # $ Alert end # BAD - Ox.parse_obj is always unsafe def route11 - xml_data = params[:key] - object = Ox.parse_obj xml_data + xml_data = params[:key] # $ Source + object = Ox.parse_obj xml_data # $ Alert end # BAD - Ox.load with :object mode is always unsafe def route12 - xml_data = params[:key] - object = Ox.load xml_data, mode: :object + xml_data = params[:key] # $ Source + object = Ox.load xml_data, mode: :object # $ Alert end # GOOD - Ox.load is safe in the default mode (which is :generic) and in any other mode than :object @@ -106,49 +106,49 @@ class UsersController < ActionController::Base # BAD - `Hash.from_trusted_xml` will deserialize elements with the # `type="yaml"` attribute as YAML. def route14 - xml = params[:key] - hash = Hash.from_trusted_xml(xml) + xml = params[:key] # $ Source + hash = Hash.from_trusted_xml(xml) # $ Alert end # BAD before psych version 4.0.0 def route15 - yaml_data = params[:key] - object = Psych.load yaml_data + yaml_data = params[:key] # $ Source + object = Psych.load yaml_data # $ Alert object = Psych.load_file yaml_data end # GOOD In psych version 4.0.0 and above def route16 - yaml_data = params[:key] - object = Psych.load yaml_data + yaml_data = params[:key] # $ Source + object = Psych.load yaml_data # $ Alert object = Psych.load_file yaml_data end # GOOD def route17 yaml_data = params[:key] - object = Psych.parse_stream(yaml_data) + object = Psych.parse_stream(yaml_data) object = Psych.parse(yaml_data) object = Psych.parse_file(yaml_data) end # BAD def route18 - yaml_data = params[:key] - object = Psych.unsafe_load(yaml_data) - object = Psych.unsafe_load_file(yaml_data) - object = Psych.load_stream(yaml_data) + yaml_data = params[:key] # $ Source + object = Psych.unsafe_load(yaml_data) # $ Alert + object = Psych.unsafe_load_file(yaml_data) # $ Alert + object = Psych.load_stream(yaml_data) # $ Alert parse_output = Psych.parse_stream(yaml_data) - object = parse_output.to_ruby - object = Psych.parse(yaml_data).to_ruby - object = Psych.parse_file(yaml_data).to_ruby + object = parse_output.to_ruby # $ Alert + object = Psych.parse(yaml_data).to_ruby # $ Alert + object = Psych.parse_file(yaml_data).to_ruby # $ Alert end # BAD def route19 - plist_data = params[:key] - result = Plist.parse_xml(plist_data) - result = Plist.parse_xml(plist_data, marshal: true) + plist_data = params[:key] # $ Source + result = Plist.parse_xml(plist_data) # $ Alert + result = Plist.parse_xml(plist_data, marshal: true) # $ Alert end # GOOD @@ -158,18 +158,18 @@ class UsersController < ActionController::Base end def stdin - object = YAML.load $stdin.read + object = YAML.load $stdin.read # $ Alert # STDIN - object = YAML.load STDIN.gets + object = YAML.load STDIN.gets # $ Alert # ARGF - object = YAML.load ARGF.read + object = YAML.load ARGF.read # $ Alert # Kernel.gets - object = YAML.load gets + object = YAML.load gets # $ Alert # Kernel.readlines - object = YAML.load readlines + object = YAML.load readlines # $ Alert end -end \ No newline at end of file +end diff --git a/ruby/ql/test/query-tests/security/cwe-506/HardcodedDataInterpretedAsCode.qlref b/ruby/ql/test/query-tests/security/cwe-506/HardcodedDataInterpretedAsCode.qlref index afa4fec282c..48494558b68 100644 --- a/ruby/ql/test/query-tests/security/cwe-506/HardcodedDataInterpretedAsCode.qlref +++ b/ruby/ql/test/query-tests/security/cwe-506/HardcodedDataInterpretedAsCode.qlref @@ -1 +1,2 @@ -queries/security/cwe-506/HardcodedDataInterpretedAsCode.ql +query: queries/security/cwe-506/HardcodedDataInterpretedAsCode.ql +postprocess: utils/test/InlineExpectationsTestQuery.ql diff --git a/ruby/ql/test/query-tests/security/cwe-506/tst.rb b/ruby/ql/test/query-tests/security/cwe-506/tst.rb index 67d117e95c4..f3392d0664d 100644 --- a/ruby/ql/test/query-tests/security/cwe-506/tst.rb +++ b/ruby/ql/test/query-tests/security/cwe-506/tst.rb @@ -2,17 +2,17 @@ def e(r) [r].pack 'H*' end -totally_harmless_string = '707574732822636f646520696e6a656374696f6e2229' +totally_harmless_string = '707574732822636f646520696e6a656374696f6e2229' # $ Source -eval(e(totally_harmless_string)) # NOT OK: eval("puts('hello'") +eval(e(totally_harmless_string)) # $ Alert // NOT OK: eval("puts('hello'") eval(totally_harmless_string) # OK: throws parse error -require e('666f6f626172') # NOT OK: require 'foobar' +require e('666f6f626172') # $ Alert // NOT OK: require 'foobar' require '666f6f626172' # OK: no taint step between source and sink x = 'deadbeef' require e(x) # OK: doesn't meet our criteria for being a source -another_questionable_string = "\x70\x75\x74\x73\x28\x27\x68\x65\x6C\x6C\x6F\x27\x29" -eval(another_questionable_string.strip) # NOT OK: eval("puts('hello'") +another_questionable_string = "\x70\x75\x74\x73\x28\x27\x68\x65\x6C\x6C\x6F\x27\x29" # $ Source +eval(another_questionable_string.strip) # $ Alert // NOT OK: eval("puts('hello'") eval(another_questionable_string) # OK: no taint step between source and sink diff --git a/ruby/ql/test/query-tests/security/cwe-598/SensitiveGetQuery.qlref b/ruby/ql/test/query-tests/security/cwe-598/SensitiveGetQuery.qlref index 98d0d8e6be7..1488e6145ba 100644 --- a/ruby/ql/test/query-tests/security/cwe-598/SensitiveGetQuery.qlref +++ b/ruby/ql/test/query-tests/security/cwe-598/SensitiveGetQuery.qlref @@ -1 +1,2 @@ -queries/security/cwe-598/SensitiveGetQuery.ql \ No newline at end of file +query: queries/security/cwe-598/SensitiveGetQuery.ql +postprocess: utils/test/InlineExpectationsTestQuery.ql diff --git a/ruby/ql/test/query-tests/security/cwe-598/app/controllers/users_controller.rb b/ruby/ql/test/query-tests/security/cwe-598/app/controllers/users_controller.rb index 441d8b493ab..1f2be8152d7 100644 --- a/ruby/ql/test/query-tests/security/cwe-598/app/controllers/users_controller.rb +++ b/ruby/ql/test/query-tests/security/cwe-598/app/controllers/users_controller.rb @@ -1,17 +1,17 @@ class UsersController < ApplicationController def login_get_1 - foo = params[:password] # BAD: route handler uses GET query parameters to receive sensitive data + foo = params[:password] # $ Alert // BAD: route handler uses GET query parameters to receive sensitive data authenticate_user(params[:username], foo) end def login_get_2 - password = params[:foo] # BAD: route handler uses GET query parameters to receive sensitive data + password = params[:foo] # $ Alert // BAD: route handler uses GET query parameters to receive sensitive data authenticate_user(params[:username], password) end def login_get_3 - @password = params[:foo] # BAD: route handler uses GET query parameters to receive sensitive data + @password = params[:foo] # $ Alert // BAD: route handler uses GET query parameters to receive sensitive data authenticate_user(params[:username], @password) end diff --git a/ruby/ql/test/query-tests/security/cwe-601/UrlRedirect.qlref b/ruby/ql/test/query-tests/security/cwe-601/UrlRedirect.qlref index 422dc00837a..76f39c8d6f3 100644 --- a/ruby/ql/test/query-tests/security/cwe-601/UrlRedirect.qlref +++ b/ruby/ql/test/query-tests/security/cwe-601/UrlRedirect.qlref @@ -1 +1,2 @@ -queries/security/cwe-601/UrlRedirect.ql +query: queries/security/cwe-601/UrlRedirect.ql +postprocess: utils/test/InlineExpectationsTestQuery.ql diff --git a/ruby/ql/test/query-tests/security/cwe-601/UrlRedirect.rb b/ruby/ql/test/query-tests/security/cwe-601/UrlRedirect.rb index 78f2248434b..5ca2ab77704 100644 --- a/ruby/ql/test/query-tests/security/cwe-601/UrlRedirect.rb +++ b/ruby/ql/test/query-tests/security/cwe-601/UrlRedirect.rb @@ -1,27 +1,27 @@ class UsersController < ActionController::Base # BAD def route1 - redirect_to params + redirect_to params # $ Alert end # BAD def route2 - redirect_to params[:key] + redirect_to params[:key] # $ Alert end # BAD def route3 - redirect_to params.fetch(:specific_arg) + redirect_to params.fetch(:specific_arg) # $ Alert end # BAD def route4 - redirect_to params.to_unsafe_hash + redirect_to params.to_unsafe_hash # $ Alert end # BAD def route5 - redirect_to filter_params(params) + redirect_to filter_params(params) # $ Alert end # GOOD @@ -31,7 +31,7 @@ class UsersController < ActionController::Base # BAD def route7 - redirect_to "#{params[:key]}/foo" + redirect_to "#{params[:key]}/foo" # $ Alert end # GOOD @@ -55,22 +55,22 @@ class UsersController < ActionController::Base # The same as `create1` but this is reachable via a GET request, as configured # by the routes at the bottom of this file. def route9 - redirect_to params[:key] + redirect_to params[:key] # $ Alert end # BAD def route10 - redirect_back fallback_location: params[:key] + redirect_back fallback_location: params[:key] # $ Alert end # BAD def route11 - redirect_back fallback_location: params[:key], allow_other_host: true + redirect_back fallback_location: params[:key], allow_other_host: true # $ Alert end # BAD def route12 - redirect_back_or_to params[:key] + redirect_back_or_to params[:key] # $ Alert end # GOOD @@ -134,4 +134,4 @@ class ConstController < ActionController::Base redirect_to "/error.html" end end -end \ No newline at end of file +end diff --git a/ruby/ql/test/query-tests/security/cwe-611/libxml-backend/LibXmlBackend.rb b/ruby/ql/test/query-tests/security/cwe-611/libxml-backend/LibXmlBackend.rb index 4e3565e149a..c7013082c77 100644 --- a/ruby/ql/test/query-tests/security/cwe-611/libxml-backend/LibXmlBackend.rb +++ b/ruby/ql/test/query-tests/security/cwe-611/libxml-backend/LibXmlBackend.rb @@ -13,11 +13,11 @@ end class LibXmlRubyXXE < ApplicationController def foo - content = params[:xml] + content = params[:xml] # $ Source - LibXML::XML::Parser.file(content, { options: 2048 }) - Hash.from_xml(content) - Hash.from_trusted_xml(content) - ActiveSupport::XmlMini.parse(content) + LibXML::XML::Parser.file(content, { options: 2048 }) # $ Alert + Hash.from_xml(content) # $ Alert + Hash.from_trusted_xml(content) # $ Alert + ActiveSupport::XmlMini.parse(content) # $ Alert end end diff --git a/ruby/ql/test/query-tests/security/cwe-611/libxml-backend/Xxe.qlref b/ruby/ql/test/query-tests/security/cwe-611/libxml-backend/Xxe.qlref index 8ed653a4869..50d9b176008 100644 --- a/ruby/ql/test/query-tests/security/cwe-611/libxml-backend/Xxe.qlref +++ b/ruby/ql/test/query-tests/security/cwe-611/libxml-backend/Xxe.qlref @@ -1 +1,2 @@ -queries/security/cwe-611/Xxe.ql +query: queries/security/cwe-611/Xxe.ql +postprocess: utils/test/InlineExpectationsTestQuery.ql diff --git a/ruby/ql/test/query-tests/security/cwe-611/xxe/LibXmlRuby.rb b/ruby/ql/test/query-tests/security/cwe-611/xxe/LibXmlRuby.rb index a8d640d62c6..2e38a92330f 100644 --- a/ruby/ql/test/query-tests/security/cwe-611/xxe/LibXmlRuby.rb +++ b/ruby/ql/test/query-tests/security/cwe-611/xxe/LibXmlRuby.rb @@ -1,15 +1,15 @@ class LibXmlRubyXXE < ApplicationController - content = params[:xml] - LibXML::XML::Document.string(content, { options: 2 | 2048, encoding: 'utf-8' }) - LibXML::XML::Document.file(content, { options: LibXML::XML::Parser::Options::NOENT | 2048}) - LibXML::XML::Document.io(content, { options: XML::Parser::Options::NOENT | 2048 }) - LibXML::XML::Parser.string(content, { options: 2 | 2048 }) - LibXML::XML::Parser.file(content, { options: 3 | 2048 }) - LibXML::XML::Parser.io(content, { options: 2 | 2048}) + content = params[:xml] # $ Source + LibXML::XML::Document.string(content, { options: 2 | 2048, encoding: 'utf-8' }) # $ Alert + LibXML::XML::Document.file(content, { options: LibXML::XML::Parser::Options::NOENT | 2048}) # $ Alert + LibXML::XML::Document.io(content, { options: XML::Parser::Options::NOENT | 2048 }) # $ Alert + LibXML::XML::Parser.string(content, { options: 2 | 2048 }) # $ Alert + LibXML::XML::Parser.file(content, { options: 3 | 2048 }) # $ Alert + LibXML::XML::Parser.io(content, { options: 2 | 2048}) # $ Alert - XML::Document.string(content, { options: 2 | 2048 }) - XML::Parser.string(content, { options: 2 | 2048 }) + XML::Document.string(content, { options: 2 | 2048 }) # $ Alert + XML::Parser.string(content, { options: 2 | 2048 }) # $ Alert LibXML::XML::Parser.file(content, { options: 2048 }) # OK diff --git a/ruby/ql/test/query-tests/security/cwe-611/xxe/Nokogiri.rb b/ruby/ql/test/query-tests/security/cwe-611/xxe/Nokogiri.rb index 76f37cfb751..f679ee9aab7 100644 --- a/ruby/ql/test/query-tests/security/cwe-611/xxe/Nokogiri.rb +++ b/ruby/ql/test/query-tests/security/cwe-611/xxe/Nokogiri.rb @@ -1,30 +1,30 @@ class NokogiriXXE < ApplicationController - content = params[:xml] + content = params[:xml] # $ Source - Nokogiri::XML::parse(content, nil, nil, 2) - Nokogiri::XML::parse(content, nil, nil, 1 | 2) - Nokogiri::XML::parse(content, nil, nil, 1 & ~Nokogiri::XML::ParseOptions::NONET) - Nokogiri::XML::parse(content, nil, nil, Nokogiri::XML::ParseOptions::NOENT) - Nokogiri::XML::parse(content, nil, nil, Nokogiri::XML::ParseOptions::DTDLOAD) + Nokogiri::XML::parse(content, nil, nil, 2) # $ Alert + Nokogiri::XML::parse(content, nil, nil, 1 | 2) # $ Alert + Nokogiri::XML::parse(content, nil, nil, 1 & ~Nokogiri::XML::ParseOptions::NONET) # $ Alert + Nokogiri::XML::parse(content, nil, nil, Nokogiri::XML::ParseOptions::NOENT) # $ Alert + Nokogiri::XML::parse(content, nil, nil, Nokogiri::XML::ParseOptions::DTDLOAD) # $ Alert Nokogiri::XML::parse(content, nil, nil, ~Nokogiri::XML::ParseOptions::NOENT) #OK - Nokogiri::XML::parse(content, nil, nil, ~Nokogiri::XML::ParseOptions::NONET) - Nokogiri::XML::parse(content, nil, nil, Nokogiri::XML::ParseOptions.new 2) + Nokogiri::XML::parse(content, nil, nil, ~Nokogiri::XML::ParseOptions::NONET) # $ Alert + Nokogiri::XML::parse(content, nil, nil, Nokogiri::XML::ParseOptions.new 2) # $ Alert options = Nokogiri::XML::ParseOptions.new 2048 options.noent - Nokogiri::XML::parse(content, nil, nil, options) - Nokogiri::XML::parse(content, nil, nil, (Nokogiri::XML::ParseOptions.new 0).noent) + Nokogiri::XML::parse(content, nil, nil, options) # $ Alert + Nokogiri::XML::parse(content, nil, nil, (Nokogiri::XML::ParseOptions.new 0).noent) # $ Alert - Nokogiri::XML::parse(content) { |x| x.noent } - Nokogiri::XML::parse(content) { |x| x.nononet } #FAIL + Nokogiri::XML::parse(content) { |x| x.noent } # $ Alert + Nokogiri::XML::parse(content) { |x| x.nononet } # $ Alert // FAIL Nokogiri::XML::parse(content) { |x| x.nodtdload } # OK - Nokogiri::XML::parse(content) { |x| x.nonet.noent.nodtdload } + Nokogiri::XML::parse(content) { |x| x.nonet.noent.nodtdload } # $ Alert Nokogiri::XML::parse(content, nil, nil, 2048) # OK - Nokogiri::XML::parse(content, nil, nil, 3) + Nokogiri::XML::parse(content, nil, nil, 3) # $ Alert Nokogiri::XML::parse(content) { |x| x.nonet.nodtdload } # OK - Nokogiri::XML::parse(content, nil, nil, Nokogiri::XML::ParseOptions::NOENT & ~Nokogiri::XML::ParseOptions::NOBLANKS) - Nokogiri::XML::parse(content, nil, nil, ~Nokogiri::XML::ParseOptions::NONET | Nokogiri::XML::ParseOptions::NOBLANKS) + Nokogiri::XML::parse(content, nil, nil, Nokogiri::XML::ParseOptions::NOENT & ~Nokogiri::XML::ParseOptions::NOBLANKS) # $ Alert + Nokogiri::XML::parse(content, nil, nil, ~Nokogiri::XML::ParseOptions::NONET | Nokogiri::XML::ParseOptions::NOBLANKS) # $ Alert end diff --git a/ruby/ql/test/query-tests/security/cwe-611/xxe/Xxe.qlref b/ruby/ql/test/query-tests/security/cwe-611/xxe/Xxe.qlref index 8ed653a4869..50d9b176008 100644 --- a/ruby/ql/test/query-tests/security/cwe-611/xxe/Xxe.qlref +++ b/ruby/ql/test/query-tests/security/cwe-611/xxe/Xxe.qlref @@ -1 +1,2 @@ -queries/security/cwe-611/Xxe.ql +query: queries/security/cwe-611/Xxe.ql +postprocess: utils/test/InlineExpectationsTestQuery.ql diff --git a/ruby/ql/test/query-tests/security/cwe-732/FilePermissions.rb b/ruby/ql/test/query-tests/security/cwe-732/FilePermissions.rb index 305bdb2d147..00530836bb0 100644 --- a/ruby/ql/test/query-tests/security/cwe-732/FilePermissions.rb +++ b/ruby/ql/test/query-tests/security/cwe-732/FilePermissions.rb @@ -2,13 +2,13 @@ require "fileutils" def run_chmod_1(filename) # BAD: sets file as world writable - FileUtils.chmod 0222, filename + FileUtils.chmod 0222, filename # $ Alert[rb/overly-permissive-file] # BAD: sets file as world writable - FileUtils.chmod 0622, filename + FileUtils.chmod 0622, filename # $ Alert[rb/overly-permissive-file] # BAD: sets file as world readable - FileUtils.chmod 0755, filename + FileUtils.chmod 0755, filename # $ Alert[rb/overly-permissive-file] # BAD: sets file as world readable + writable - FileUtils.chmod 0777, filename + FileUtils.chmod 0777, filename # $ Alert[rb/overly-permissive-file] end module DummyModule @@ -25,7 +25,7 @@ def run_chmod_2(filename) baz.chmod 0755, filename baz = bar # BAD: sets file as world readable - baz.chmod 0755, filename + baz.chmod 0755, filename # $ Alert[rb/overly-permissive-file] end def run_chmod_3(filename) @@ -48,26 +48,26 @@ def run_chmod_4(filename) end def run_chmod_5(filename) - perm = 0777 + perm = 0777 # $ Alert[rb/overly-permissive-file] # BAD: sets world rwx - FileUtils.chmod perm, filename + FileUtils.chmod perm, filename # $ Sink[rb/overly-permissive-file] perm2 = perm # BAD: sets world rwx - FileUtils.chmod perm2, filename + FileUtils.chmod perm2, filename # $ Sink[rb/overly-permissive-file] - perm = "u=wrx,g=rwx,o=x" + perm = "u=wrx,g=rwx,o=x" # $ Alert[rb/overly-permissive-file] perm2 = perm # BAD: sets group rwx - FileUtils.chmod perm2, filename + FileUtils.chmod perm2, filename # $ Sink[rb/overly-permissive-file] # BAD: sets file as world readable - FileUtils.chmod "u=rwx,o+r", filename + FileUtils.chmod "u=rwx,o+r", filename # $ Alert[rb/overly-permissive-file] # GOOD: sets file as group/world unreadable FileUtils.chmod "u=rwx,go-r", filename # BAD: sets group/world as +rw - FileUtils.chmod "a+rw", filename + FileUtils.chmod "a+rw", filename # $ Alert[rb/overly-permissive-file] end def run_chmod_R(filename) # BAD: sets file as world readable - FileUtils.chmod_R 0755, filename + FileUtils.chmod_R 0755, filename # $ Alert[rb/overly-permissive-file] end diff --git a/ruby/ql/test/query-tests/security/cwe-732/WeakCookieConfiguration.qlref b/ruby/ql/test/query-tests/security/cwe-732/WeakCookieConfiguration.qlref index 7c8c5ca3c93..94f0b0dac3c 100644 --- a/ruby/ql/test/query-tests/security/cwe-732/WeakCookieConfiguration.qlref +++ b/ruby/ql/test/query-tests/security/cwe-732/WeakCookieConfiguration.qlref @@ -1 +1,2 @@ -queries/security/cwe-732/WeakCookieConfiguration.ql +query: queries/security/cwe-732/WeakCookieConfiguration.ql +postprocess: utils/test/InlineExpectationsTestQuery.ql diff --git a/ruby/ql/test/query-tests/security/cwe-732/WeakFilePermissions.qlref b/ruby/ql/test/query-tests/security/cwe-732/WeakFilePermissions.qlref index bf19b31509d..baceccada54 100644 --- a/ruby/ql/test/query-tests/security/cwe-732/WeakFilePermissions.qlref +++ b/ruby/ql/test/query-tests/security/cwe-732/WeakFilePermissions.qlref @@ -1 +1,2 @@ -queries/security/cwe-732/WeakFilePermissions.ql +query: queries/security/cwe-732/WeakFilePermissions.ql +postprocess: utils/test/InlineExpectationsTestQuery.ql diff --git a/ruby/ql/test/query-tests/security/cwe-732/app/config/application.rb b/ruby/ql/test/query-tests/security/cwe-732/app/config/application.rb index 5b5604f4d78..e6993033b22 100644 --- a/ruby/ql/test/query-tests/security/cwe-732/app/config/application.rb +++ b/ruby/ql/test/query-tests/security/cwe-732/app/config/application.rb @@ -11,16 +11,16 @@ module App config.action_dispatch.encrypted_cookie_cipher = "ChaCha" # BAD: weak block encryption algorithm - config.action_dispatch.encrypted_cookie_cipher = "DES" + config.action_dispatch.encrypted_cookie_cipher = "DES" # $ Alert[rb/weak-cookie-configuration] # BAD: weak block encryption mode - config.action_dispatch.encrypted_cookie_cipher = "AES-256-ECB" + config.action_dispatch.encrypted_cookie_cipher = "AES-256-ECB" # $ Alert[rb/weak-cookie-configuration] # GOOD config.action_dispatch.use_authenticated_cookie_encryption = true # BAD: less secure block encryption mode - config.action_dispatch.use_authenticated_cookie_encryption = false + config.action_dispatch.use_authenticated_cookie_encryption = false # $ Alert[rb/weak-cookie-configuration] # GOOD config.action_dispatch.cookies_same_site_protection = :lax @@ -29,9 +29,9 @@ module App config.action_dispatch.cookies_same_site_protection = "strict" # BAD: disabling same-site protections for sending cookies - config.action_dispatch.cookies_same_site_protection = :none + config.action_dispatch.cookies_same_site_protection = :none # $ Alert[rb/weak-cookie-configuration] # BAD: not all browsers default to `lax` if unset - config.action_dispatch.cookies_same_site_protection = nil + config.action_dispatch.cookies_same_site_protection = nil # $ Alert[rb/weak-cookie-configuration] end end diff --git a/ruby/ql/test/query-tests/security/cwe-798/HardcodedCredentials.qlref b/ruby/ql/test/query-tests/security/cwe-798/HardcodedCredentials.qlref index e65b7754872..81afcc528c8 100644 --- a/ruby/ql/test/query-tests/security/cwe-798/HardcodedCredentials.qlref +++ b/ruby/ql/test/query-tests/security/cwe-798/HardcodedCredentials.qlref @@ -1 +1,2 @@ -queries/security/cwe-798/HardcodedCredentials.ql +query: queries/security/cwe-798/HardcodedCredentials.ql +postprocess: utils/test/InlineExpectationsTestQuery.ql diff --git a/ruby/ql/test/query-tests/security/cwe-798/HardcodedCredentials.rb b/ruby/ql/test/query-tests/security/cwe-798/HardcodedCredentials.rb index 57f05a25fdf..b726300559e 100644 --- a/ruby/ql/test/query-tests/security/cwe-798/HardcodedCredentials.rb +++ b/ruby/ql/test/query-tests/security/cwe-798/HardcodedCredentials.rb @@ -1,24 +1,24 @@ -def authenticate(uid, password, cert: nil) +def authenticate(uid, password, cert: nil) # $ Sink if cert != nil then # comparison with hardcoded credential - return cert == "xwjVWdfzfRlbcgKkbSfG/xSrUeHYqxPgz9WKN3Yow1o=" + return cert == "xwjVWdfzfRlbcgKkbSfG/xSrUeHYqxPgz9WKN3Yow1o=" # $ Alert end # comparison with hardcoded credential - uid == 123 and password == "X6BLgRWSAtAWG/GaHS+WGGW2K7zZFTAjJ54fGSudHJk=" + uid == 123 and password == "X6BLgRWSAtAWG/GaHS+WGGW2K7zZFTAjJ54fGSudHJk=" # $ Alert end # call with hardcoded credential as argument -authenticate(123, "4NQX/CqB5Ae98zFUmwj1DMpF7azshxSvb0Jo4gIFmIQ=") +authenticate(123, "4NQX/CqB5Ae98zFUmwj1DMpF7azshxSvb0Jo4gIFmIQ=") # $ Alert # call with hardcoded credential as argument -authenticate(456, nil, cert: "WLC17dLQ9P8YlQvqm77qplOMm5pd1q25Q2onWqu78JI=") +authenticate(456, nil, cert: "WLC17dLQ9P8YlQvqm77qplOMm5pd1q25Q2onWqu78JI=") # $ Alert # concatenation involving literal -authenticate(789, "pw:" + "ogH6qSYWGdbR/2WOGYa7eZ/tObL+GtqDPx6q37BTTRQ=") +authenticate(789, "pw:" + "ogH6qSYWGdbR/2WOGYa7eZ/tObL+GtqDPx6q37BTTRQ=") # $ Alert -pw_left = "3jOe7sXKX6Tx52qHWUVqh2t9LNsE+ZXFj2qw6asRARTV2deAXFKkMTVOoaFYom1Q" -pw_right = "4fQuzXef4f2yow8KWvIJTA==" +pw_left = "3jOe7sXKX6Tx52qHWUVqh2t9LNsE+ZXFj2qw6asRARTV2deAXFKkMTVOoaFYom1Q" # $ Alert +pw_right = "4fQuzXef4f2yow8KWvIJTA==" # $ Alert pw = pw_left + pw_right authenticate(999, pw) @@ -28,18 +28,18 @@ authenticate("gowLsSGfPbh/ZS60k+LQQBhcq1tsh/YgbvNmDauQr5Q=", passwd) module Passwords class KnownPasswords - def include?(passwd) + def include?(passwd) # $ Sink passwd == "foo" end end end # Call to object method -Passwords::KnownPasswords.new.include?("kdW/xVhiv6y1fQQNevDpUaq+2rfPKfh+teE/45zS7bc=") +Passwords::KnownPasswords.new.include?("kdW/xVhiv6y1fQQNevDpUaq+2rfPKfh+teE/45zS7bc=") # $ Alert # Call to unrelated method with same name (should not be flagged) "foobar".include?("foo") -def default_cred(username = "user@test.com", password = "abcdef123456") +def default_cred(username = "user@test.com", password = "abcdef123456") # $ Alert username -end \ No newline at end of file +end diff --git a/ruby/ql/test/query-tests/security/cwe-807-user-controlled-bypass/ConditionalBypass.qlref b/ruby/ql/test/query-tests/security/cwe-807-user-controlled-bypass/ConditionalBypass.qlref index 9639e207d1e..5b8e3bc44f1 100644 --- a/ruby/ql/test/query-tests/security/cwe-807-user-controlled-bypass/ConditionalBypass.qlref +++ b/ruby/ql/test/query-tests/security/cwe-807-user-controlled-bypass/ConditionalBypass.qlref @@ -1 +1,2 @@ -experimental/cwe-807/ConditionalBypass.ql \ No newline at end of file +query: experimental/cwe-807/ConditionalBypass.ql +postprocess: utils/test/InlineExpectationsTestQuery.ql diff --git a/ruby/ql/test/query-tests/security/cwe-807-user-controlled-bypass/ConditionalBypass.rb b/ruby/ql/test/query-tests/security/cwe-807-user-controlled-bypass/ConditionalBypass.rb index 1bd45f15043..b6e2b6a50ab 100644 --- a/ruby/ql/test/query-tests/security/cwe-807-user-controlled-bypass/ConditionalBypass.rb +++ b/ruby/ql/test/query-tests/security/cwe-807-user-controlled-bypass/ConditionalBypass.rb @@ -1,9 +1,9 @@ class FooController < ActionController::Base def bad_handler1 - check = params[:check] + check = params[:check] # $ Source name = params[:name] - if check + if check # $ Alert # BAD authenticate_user! name end @@ -11,20 +11,20 @@ class FooController < ActionController::Base def bad_handler2 # BAD - login if params[:login] + login if params[:login] # $ Alert do_something_else end def bad_handler3 # BAD. Not detected: its the last statement in the method, so it doesn't # match the heuristic for an action. - login if params[:login] + login if params[:login] # $ MISSING: Alert end def bad_handler4 - p = (params[:name] == "foo") + p = (params[:name] == "foo") # $ Source # BAD - if p + if p # $ Alert verify! end end diff --git a/ruby/ql/test/query-tests/security/cwe-912/HttpToFileAccess.qlref b/ruby/ql/test/query-tests/security/cwe-912/HttpToFileAccess.qlref index 2b41f979bb5..06312044c51 100644 --- a/ruby/ql/test/query-tests/security/cwe-912/HttpToFileAccess.qlref +++ b/ruby/ql/test/query-tests/security/cwe-912/HttpToFileAccess.qlref @@ -1 +1,2 @@ -queries/security/cwe-912/HttpToFileAccess.ql \ No newline at end of file +query: queries/security/cwe-912/HttpToFileAccess.ql +postprocess: utils/test/InlineExpectationsTestQuery.ql diff --git a/ruby/ql/test/query-tests/security/cwe-912/http_to_file_access.rb b/ruby/ql/test/query-tests/security/cwe-912/http_to_file_access.rb index aa8ce4c46ff..062ff36c657 100644 --- a/ruby/ql/test/query-tests/security/cwe-912/http_to_file_access.rb +++ b/ruby/ql/test/query-tests/security/cwe-912/http_to_file_access.rb @@ -1,14 +1,14 @@ require "net/http" -resp = Net::HTTP.new("evil.com").get("/script").body +resp = Net::HTTP.new("evil.com").get("/script").body # $ Source file = File.open("/tmp/script", "w") -file.write(resp) # BAD +file.write(resp) # $ Alert // BAD class ExampleController < ActionController::Base def example - script = params[:script] + script = params[:script] # $ Source file = File.open("/tmp/script", "w") - file.write(script) # BAD + file.write(script) # $ Alert // BAD end def example2 @@ -16,4 +16,4 @@ class ExampleController < ActionController::Base file = File.open("/tmp/script", "w") file.write(a) # GOOD end -end \ No newline at end of file +end diff --git a/ruby/ql/test/query-tests/security/cwe-915/MassAssignment.qlref b/ruby/ql/test/query-tests/security/cwe-915/MassAssignment.qlref index 89dbc405a3a..d60d17065b7 100644 --- a/ruby/ql/test/query-tests/security/cwe-915/MassAssignment.qlref +++ b/ruby/ql/test/query-tests/security/cwe-915/MassAssignment.qlref @@ -1 +1,2 @@ -queries/security/cwe-915/MassAssignment.ql \ No newline at end of file +query: queries/security/cwe-915/MassAssignment.ql +postprocess: utils/test/InlineExpectationsTestQuery.ql diff --git a/ruby/ql/test/query-tests/security/cwe-915/test.rb b/ruby/ql/test/query-tests/security/cwe-915/test.rb index c72ad536ef7..d6193e27236 100644 --- a/ruby/ql/test/query-tests/security/cwe-915/test.rb +++ b/ruby/ql/test/query-tests/security/cwe-915/test.rb @@ -5,7 +5,7 @@ end class UserController < ActionController::Base def create # BAD: arbitrary params are permitted to be used for this assignment - User.new(user_params).save! + User.new(user_params).save! # $ Alert end def create2 @@ -15,42 +15,42 @@ class UserController < ActionController::Base def create3 # each BAD - User.build(user_params) - User.create(user_params) - User.create!(user_params) - User.insert(user_params) - User.insert!(user_params) + User.build(user_params) # $ Alert + User.create(user_params) # $ Alert + User.create!(user_params) # $ Alert + User.insert(user_params) # $ Alert + User.insert!(user_params) # $ Alert User.insert_all([user_params]) User.insert_all!([user_params]) - User.update(user_params) - User.update(7, user_params) - User.update!(user_params) - User.update!(7, user_params) - User.upsert(user_params) + User.update(user_params) # $ Alert + User.update(7, user_params) # $ Alert + User.update!(user_params) # $ Alert + User.update!(7, user_params) # $ Alert + User.upsert(user_params) # $ Alert User.upsert([user_params]) - User.find_or_create_by(user_params) - User.find_or_create_by!(user_params) - User.find_or_initialize_by(user_params) - User.create_or_find_by(user_params) - User.create_or_find_by!(user_params) - User.create_with(user_params) + User.find_or_create_by(user_params) # $ Alert + User.find_or_create_by!(user_params) # $ Alert + User.find_or_initialize_by(user_params) # $ Alert + User.create_or_find_by(user_params) # $ Alert + User.create_or_find_by!(user_params) # $ Alert + User.create_with(user_params) # $ Alert user = User.where(name:"abc") user.update(user_params) end def user_params - params.require(:user).permit! + params.require(:user).permit! # $ Source end def create4 - x = params[:user] + x = params[:user] # $ Source x.permit! - User.new(x) # BAD + User.new(x) # $ Alert // BAD User.new(x.permit(:name,:address)) # GOOD - User.new(params.permit(user: {})) # BAD - User.new(params.permit(user: [:name, :address, {friends:{}}])) # BAD - User.new(params.to_unsafe_h) # BAD + User.new(params.permit(user: {})) # $ Alert // BAD + User.new(params.permit(user: [:name, :address, {friends:{}}])) # $ Alert // BAD + User.new(params.to_unsafe_h) # $ Alert // BAD User.new(params.permit(user: [:name, :address]).to_unsafe_h) # GOOD end -end \ No newline at end of file +end diff --git a/ruby/ql/test/query-tests/security/cwe-918/ServerSideRequestForgery.qlref b/ruby/ql/test/query-tests/security/cwe-918/ServerSideRequestForgery.qlref index 34f3a2952f2..615ca40af22 100644 --- a/ruby/ql/test/query-tests/security/cwe-918/ServerSideRequestForgery.qlref +++ b/ruby/ql/test/query-tests/security/cwe-918/ServerSideRequestForgery.qlref @@ -1 +1,2 @@ -queries/security/cwe-918/ServerSideRequestForgery.ql +query: queries/security/cwe-918/ServerSideRequestForgery.ql +postprocess: utils/test/InlineExpectationsTestQuery.ql diff --git a/ruby/ql/test/query-tests/security/cwe-918/ServerSideRequestForgery.rb b/ruby/ql/test/query-tests/security/cwe-918/ServerSideRequestForgery.rb index ff99ffe1801..f2ff6825b7d 100644 --- a/ruby/ql/test/query-tests/security/cwe-918/ServerSideRequestForgery.rb +++ b/ruby/ql/test/query-tests/security/cwe-918/ServerSideRequestForgery.rb @@ -7,17 +7,17 @@ class PostsController < ActionController::Base user = params[:user_id] # BAD - user can control the entire URL of the request - users_service_domain = params[:users_service_domain] - response = Excon.post("#{users_service_domain}/logins", body: {user_id: user}).body + users_service_domain = params[:users_service_domain] # $ Source + response = Excon.post("#{users_service_domain}/logins", body: {user_id: user}).body # $ Alert token = JSON.parse(response)["token"] # BAD - user can control the entire URL for the request using Faraday library - conn = Faraday.new(url: params[:url]) + conn = Faraday.new(url: params[:url]) # $ Alert resp = conn.post token = JSON.parse(resp)["token"] # BAD - user can control the entire URL for the request using Faraday::Connection library - conn = Faraday::Connection.new(url: params[:url]) + conn = Faraday::Connection.new(url: params[:url]) # $ Alert resp = conn.post token = JSON.parse(resp)["token"] diff --git a/ruby/ql/test/query-tests/security/decompression-api/DecompressionApi.qlref b/ruby/ql/test/query-tests/security/decompression-api/DecompressionApi.qlref index feb45b82220..4d63d1ce624 100644 --- a/ruby/ql/test/query-tests/security/decompression-api/DecompressionApi.qlref +++ b/ruby/ql/test/query-tests/security/decompression-api/DecompressionApi.qlref @@ -1 +1,2 @@ -experimental/decompression-api/DecompressionApi.ql \ No newline at end of file +query: experimental/decompression-api/DecompressionApi.ql +postprocess: utils/test/InlineExpectationsTestQuery.ql diff --git a/ruby/ql/test/query-tests/security/decompression-api/decompression_api.rb b/ruby/ql/test/query-tests/security/decompression-api/decompression_api.rb index 6c1daa144e2..83f05073747 100644 --- a/ruby/ql/test/query-tests/security/decompression-api/decompression_api.rb +++ b/ruby/ql/test/query-tests/security/decompression-api/decompression_api.rb @@ -1,8 +1,8 @@ class TestController < ActionController::Base # this should get picked up def unsafe_zlib_unzip - path = params[:file] - Zlib::Inflate.inflate(path) + path = params[:file] # $ Source + Zlib::Inflate.inflate(path) # $ Alert end # this should not get picked up @@ -12,11 +12,11 @@ class TestController < ActionController::Base # this should get picked up def unsafe_zlib_unzip - Zip::File.open_buffer(params[:file]) + Zip::File.open_buffer(params[:file]) # $ Alert end # this should not get picked up def safe_zlib_unzip Zip::File.open_buffer(file) end -end \ No newline at end of file +end diff --git a/shared/tree-sitter-extractor/src/extractor/mod.rs b/shared/tree-sitter-extractor/src/extractor/mod.rs index e8e608c3244..436ff9f65a1 100644 --- a/shared/tree-sitter-extractor/src/extractor/mod.rs +++ b/shared/tree-sitter-extractor/src/extractor/mod.rs @@ -333,6 +333,9 @@ pub fn extract( .run_from_tree(&tree, source) .unwrap_or_else(|e| panic!("Desugaring failed for {path_str}: {e}")); traverse_yeast(&ast, &mut visitor); + // Comments and other `extra` nodes are not represented in the desugared + // AST, so recover them directly from the original parse tree. + traverse_extras(&tree, &mut visitor); } else { traverse(&tree, &mut visitor); } @@ -365,6 +368,8 @@ struct Visitor<'a> { ast_node_parent_table_name: String, /// Language-specific name of the tokeninfo table tokeninfo_table_name: String, + /// Language-specific name of the trivia tokeninfo table + trivia_tokeninfo_table_name: String, /// A lookup table from type name to node types schema: &'a NodeTypeMap, /// A stack for gathering information from child nodes. Whenever a node is @@ -395,11 +400,33 @@ impl<'a> Visitor<'a> { ast_node_location_table_name: format!("{language_prefix}_ast_node_location"), ast_node_parent_table_name: format!("{language_prefix}_ast_node_parent"), tokeninfo_table_name: format!("{language_prefix}_tokeninfo"), + trivia_tokeninfo_table_name: format!("{language_prefix}_trivia_tokeninfo"), schema, stack: Vec::new(), } } + /// Emits a `TriviaToken` for the given `extra` node (e.g. a comment) from + /// the original parse tree. Trivia tokens carry a location and their source + /// text, but are not attached to a parent in the (possibly desugared) AST. + fn emit_trivia_token(&mut self, node: &Node) { + let id = self.trap_writer.fresh_id(); + let loc = location_for(self, self.file_label, node); + let loc_label = location_label(self.trap_writer, loc); + self.trap_writer.add_tuple( + &self.ast_node_location_table_name, + vec![trap::Arg::Label(id), trap::Arg::Label(loc_label)], + ); + self.trap_writer.add_tuple( + &self.trivia_tokeninfo_table_name, + vec![ + trap::Arg::Label(id), + trap::Arg::Int(node.kind_id() as usize), + sliced_source_arg(self.source, node), + ], + ); + } + fn record_parse_error(&mut self, loc: trap::Label, mesg: &diagnostics::DiagnosticMessage) { self.diagnostics_writer.write(mesg); let id = self.trap_writer.fresh_id(); @@ -835,6 +862,24 @@ fn traverse(tree: &Tree, visitor: &mut Visitor) { } } +/// Walks the original tree-sitter tree and emits a `TriviaToken` for every +/// `extra` node (e.g. a comment). Used to preserve comments that would +/// otherwise be lost after a desugaring pass rewrites the tree. +fn traverse_extras(tree: &Tree, visitor: &mut Visitor) { + emit_extras_in(visitor, tree.root_node()); +} + +fn emit_extras_in(visitor: &mut Visitor, node: Node<'_>) { + let mut cursor = node.walk(); + for child in node.children(&mut cursor) { + if child.is_extra() { + visitor.emit_trivia_token(&child); + } else { + emit_extras_in(visitor, child); + } + } +} + fn traverse_yeast(tree: &yeast::Ast, visitor: &mut Visitor) { use yeast::Cursor; let mut cursor = tree.walk(); diff --git a/shared/tree-sitter-extractor/src/generator/mod.rs b/shared/tree-sitter-extractor/src/generator/mod.rs index da13322fe60..d3880a74579 100644 --- a/shared/tree-sitter-extractor/src/generator/mod.rs +++ b/shared/tree-sitter-extractor/src/generator/mod.rs @@ -68,7 +68,12 @@ pub fn generate( let node_parent_table_name = format!("{}_ast_node_parent", &prefix); let token_name = format!("{}_token", &prefix); let tokeninfo_name = format!("{}_tokeninfo", &prefix); + let trivia_token_name = format!("{}_trivia_token", &prefix); + let trivia_tokeninfo_name = format!("{}_trivia_tokeninfo", &prefix); let reserved_word_name = format!("{}_reserved_word", &prefix); + // When a desugaring is configured, comments and other `extra` nodes are + // preserved from the original parse tree as `TriviaToken`s. + let has_trivia_tokens = language.desugar.is_some(); let effective_node_types: String = match language .desugar .as_ref() @@ -85,28 +90,35 @@ pub fn generate( let nodes = node_types::read_node_types_str(&prefix, &effective_node_types)?; let (dbscheme_entries, mut ast_node_members, token_kinds) = convert_nodes(&nodes); ast_node_members.insert(&token_name); + if has_trivia_tokens { + ast_node_members.insert(&trivia_token_name); + } writeln!(&mut dbscheme_writer, "/*- {} dbscheme -*/", language.name)?; dbscheme::write(&mut dbscheme_writer, &dbscheme_entries)?; let token_case = create_token_case(&token_name, token_kinds); - dbscheme::write( - &mut dbscheme_writer, - &[ - dbscheme::Entry::Table(create_tokeninfo(&tokeninfo_name, &token_name)), - dbscheme::Entry::Case(token_case), - dbscheme::Entry::Union(dbscheme::Union { - name: &ast_node_name, - members: ast_node_members, - }), - dbscheme::Entry::Table(create_ast_node_location_table( - &node_location_table_name, - &ast_node_name, - )), - dbscheme::Entry::Table(create_ast_node_parent_table( - &node_parent_table_name, - &ast_node_name, - )), - ], - )?; + let mut dbscheme_tail = vec![ + dbscheme::Entry::Table(create_tokeninfo(&tokeninfo_name, &token_name)), + dbscheme::Entry::Case(token_case), + ]; + if has_trivia_tokens { + dbscheme_tail.push(dbscheme::Entry::Table(create_tokeninfo( + &trivia_tokeninfo_name, + &trivia_token_name, + ))); + } + dbscheme_tail.push(dbscheme::Entry::Union(dbscheme::Union { + name: &ast_node_name, + members: ast_node_members, + })); + dbscheme_tail.push(dbscheme::Entry::Table(create_ast_node_location_table( + &node_location_table_name, + &ast_node_name, + ))); + dbscheme_tail.push(dbscheme::Entry::Table(create_ast_node_parent_table( + &node_parent_table_name, + &ast_node_name, + ))); + dbscheme::write(&mut dbscheme_writer, &dbscheme_tail)?; let mut body = vec![ ql::TopLevel::Class(ql_gen::create_ast_node_class( @@ -116,6 +128,12 @@ pub fn generate( )), ql::TopLevel::Class(ql_gen::create_token_class(&token_name, &tokeninfo_name)), ]; + if has_trivia_tokens { + body.push(ql::TopLevel::Class(ql_gen::create_trivia_token_class( + &trivia_token_name, + &trivia_tokeninfo_name, + ))); + } // Only emit the ReservedWord class when there are actually unnamed token // types in the schema (i.e., @{prefix}_reserved_word exists in the dbscheme). // When converting from a YEAST YAML schema that has no unnamed tokens, this diff --git a/shared/tree-sitter-extractor/src/generator/ql_gen.rs b/shared/tree-sitter-extractor/src/generator/ql_gen.rs index bb990beacc8..f827b12580e 100644 --- a/shared/tree-sitter-extractor/src/generator/ql_gen.rs +++ b/shared/tree-sitter-extractor/src/generator/ql_gen.rs @@ -199,6 +199,70 @@ pub fn create_token_class<'a>(token_type: &'a str, tokeninfo: &'a str) -> ql::Cl } } +/// Creates the `TriviaToken` class. Trivia tokens (e.g. comments) are +/// `extra` nodes preserved from the original parse tree even when the tree has +/// been rewritten by a desugaring pass. They are not part of the regular +/// `Token` hierarchy because they do not appear in the (possibly desugared) +/// output schema. +pub fn create_trivia_token_class<'a>( + trivia_token_type: &'a str, + trivia_tokeninfo: &'a str, +) -> ql::Class<'a> { + let trivia_tokeninfo_arity = 3; // id, kind, value + let get_value = ql::Predicate { + qldoc: Some(String::from("Gets the source text of this trivia token.")), + name: "getValue", + overridden: false, + is_private: false, + is_final: true, + return_type: Some(ql::Type::String), + formal_parameters: vec![], + body: create_get_field_expr_for_column_storage( + "result", + trivia_tokeninfo, + 1, + trivia_tokeninfo_arity, + ), + overlay: None, + }; + let to_string = ql::Predicate { + qldoc: Some(String::from( + "Gets a string representation of this element.", + )), + name: "toString", + overridden: true, + is_private: false, + is_final: true, + return_type: Some(ql::Type::String), + formal_parameters: vec![], + body: ql::Expression::Equals( + Box::new(ql::Expression::Var("result")), + Box::new(ql::Expression::Dot( + Box::new(ql::Expression::Var("this")), + "getValue", + vec![], + )), + ), + overlay: None, + }; + ql::Class { + qldoc: Some(String::from( + "A trivia token, such as a comment, preserved from the original parse tree.", + )), + name: "TriviaToken", + is_abstract: false, + supertypes: vec![ql::Type::At(trivia_token_type), ql::Type::Normal("AstNode")] + .into_iter() + .collect(), + characteristic_predicate: None, + predicates: vec![ + get_value, + to_string, + create_get_a_primary_ql_class("TriviaToken", false), + ], + } +} + // Creates the `ReservedWord` class. pub fn create_reserved_word_class(db_name: &str) -> ql::Class<'_> { let class_name = "ReservedWord"; diff --git a/shared/typeflow/codeql/typeflow/TypeFlow.qll b/shared/typeflow/codeql/typeflow/TypeFlow.qll index 52a91197409..d34604fcc56 100644 --- a/shared/typeflow/codeql/typeflow/TypeFlow.qll +++ b/shared/typeflow/codeql/typeflow/TypeFlow.qll @@ -29,6 +29,12 @@ signature module TypeFlowInput { Location getLocation(); } + /** + * Gets an identifier for node `n`, if any. When no identifier is provided for `n`, + * the library falls back to location-based ranking. + */ + default int getTypeFlowNodeId(TypeFlowNode n) { none() } + /** * Holds if data can flow from `n1` to `n2` in one step. * diff --git a/shared/typeflow/codeql/typeflow/UniversalFlow.qll b/shared/typeflow/codeql/typeflow/UniversalFlow.qll index e5f07690a18..64a0ed846a0 100644 --- a/shared/typeflow/codeql/typeflow/UniversalFlow.qll +++ b/shared/typeflow/codeql/typeflow/UniversalFlow.qll @@ -45,6 +45,12 @@ signature module UniversalFlowInput { Location getLocation(); } + /** + * Gets an identifier for node `n`, if any. When no identifier is provided for `n`, + * the library falls back to location-based ranking. + */ + default int getFlowNodeId(FlowNode n) { none() } + /** * Holds if data can flow from `n1` to `n2` in one step. * @@ -149,17 +155,44 @@ module Make I> { private module RankEdge implements RankedEdge { private import E + private predicate needsNodeId(FlowNode n) { edge(n, _) } + + private int getFlowNodeIdByLoc(FlowNode n) { + n = + rank[result](FlowNode n0, string filePath, int startline, int startcolumn | + needsNodeId(n0) and + not exists(getFlowNodeId(n0)) and + n0.getLocation().hasLocationInfo(filePath, startline, startcolumn, _, _) + | + n0 order by filePath, startline, startcolumn + ) + } + + private int getFlowNodeIdExt(FlowNode n) { + n = + rank[result](FlowNode n0, int a, int b | + needsNodeId(n0) and + a = 0 and + b = getFlowNodeId(n0) + or + a = 1 and + b = getFlowNodeIdByLoc(n0) + | + n0 order by a, b + ) + } + /** * Holds if `r` is a ranking of the incoming edges `(n1,n2)` to `n2`. The used * ordering is not necessarily total, so the ranking may have gaps. */ private predicate edgeRank1(int r, FlowNode n1, Node n2) { n1 = - rank[r](FlowNode n, int startline, int startcolumn | + rank[r](FlowNode n, int id | edge(n, n2) and - n.getLocation().hasLocationInfo(_, startline, startcolumn, _, _) + id = getFlowNodeIdExt(n) | - n order by startline, startcolumn + n order by id ) } diff --git a/shared/typeflow/codeql/typeflow/internal/TypeFlowImpl.qll b/shared/typeflow/codeql/typeflow/internal/TypeFlowImpl.qll index 437e1ab3199..71b530b143e 100644 --- a/shared/typeflow/codeql/typeflow/internal/TypeFlowImpl.qll +++ b/shared/typeflow/codeql/typeflow/internal/TypeFlowImpl.qll @@ -12,6 +12,8 @@ module TypeFlow I> { private module UfInput implements UniversalFlow::UniversalFlowInput { class FlowNode = TypeFlowNode; + predicate getFlowNodeId = I::getTypeFlowNodeId/1; + predicate step = I::step/2; predicate isNullValue = I::isNullValue/1; diff --git a/swift/ql/integration-tests/osx/hello-xcode/Files.ql b/swift/ql/integration-tests/osx/hello-xcode/Files.ql index 0ef87821564..1151ff0bb9b 100644 --- a/swift/ql/integration-tests/osx/hello-xcode/Files.ql +++ b/swift/ql/integration-tests/osx/hello-xcode/Files.ql @@ -1,5 +1,7 @@ import swift from File f -where exists(f.getRelativePath()) or f instanceof UnknownFile +where + (exists(f.getRelativePath()) or f instanceof UnknownFile) and + not f.getBaseName() = "" select f diff --git a/swift/ql/lib/utils/test/internal/InlineExpectationsTestImpl.qll b/swift/ql/lib/utils/test/internal/InlineExpectationsTestImpl.qll index af84a908633..b96f27c42ac 100644 --- a/swift/ql/lib/utils/test/internal/InlineExpectationsTestImpl.qll +++ b/swift/ql/lib/utils/test/internal/InlineExpectationsTestImpl.qll @@ -15,7 +15,7 @@ module Impl implements InlineExpectationsTestSig { ExpectationComment() { this = MkExpectationComment(comment) } /** Returns the contents of the given comment, _without_ the preceding comment marker (`//`). */ - string getContents() { result = comment.getText().suffix(2) } + string getContents() { result = comment.getText().suffix(2).trim() } /** Gets a textual representation of this element. */ string toString() { result = comment.toString() } diff --git a/swift/ql/test/query-tests/Security/CWE-020/IncompleteHostnameRegex.qlref b/swift/ql/test/query-tests/Security/CWE-020/IncompleteHostnameRegex.qlref index b80ac364258..6b46d67a849 100644 --- a/swift/ql/test/query-tests/Security/CWE-020/IncompleteHostnameRegex.qlref +++ b/swift/ql/test/query-tests/Security/CWE-020/IncompleteHostnameRegex.qlref @@ -1 +1,2 @@ -queries/Security/CWE-020/IncompleteHostnameRegex.ql +query: queries/Security/CWE-020/IncompleteHostnameRegex.ql +postprocess: utils/test/InlineExpectationsTestQuery.ql diff --git a/swift/ql/test/query-tests/Security/CWE-020/MissingRegexAnchor.qlref b/swift/ql/test/query-tests/Security/CWE-020/MissingRegexAnchor.qlref index 9b1f04d1a7a..4e76e1995e9 100644 --- a/swift/ql/test/query-tests/Security/CWE-020/MissingRegexAnchor.qlref +++ b/swift/ql/test/query-tests/Security/CWE-020/MissingRegexAnchor.qlref @@ -1 +1,2 @@ -queries/Security/CWE-020/MissingRegexAnchor.ql +query: queries/Security/CWE-020/MissingRegexAnchor.ql +postprocess: utils/test/InlineExpectationsTestQuery.ql diff --git a/swift/ql/test/query-tests/Security/CWE-020/SemiAnchoredRegex.swift b/swift/ql/test/query-tests/Security/CWE-020/SemiAnchoredRegex.swift index 3b0abe53048..d588e1d6439 100644 --- a/swift/ql/test/query-tests/Security/CWE-020/SemiAnchoredRegex.swift +++ b/swift/ql/test/query-tests/Security/CWE-020/SemiAnchoredRegex.swift @@ -47,64 +47,64 @@ class NSString : NSObject { func tests(input: String) throws { _ = try Regex("^a|").firstMatch(in: input) - _ = try Regex("^a|b").firstMatch(in: input) // BAD (missing anchor) + _ = try Regex("^a|b").firstMatch(in: input) // $ Alert[swift/missing-regexp-anchor] // BAD (missing anchor) _ = try Regex("a|^b").firstMatch(in: input) _ = try Regex("^a|^b").firstMatch(in: input) - _ = try Regex("^a|b|c").firstMatch(in: input) // BAD (missing anchor) + _ = try Regex("^a|b|c").firstMatch(in: input) // $ Alert[swift/missing-regexp-anchor] // BAD (missing anchor) _ = try Regex("a|^b|c").firstMatch(in: input) _ = try Regex("a|b|^c").firstMatch(in: input) _ = try Regex("^a|^b|c").firstMatch(in: input) _ = try Regex("(^a)|b").firstMatch(in: input) - _ = try Regex("^a|(b)").firstMatch(in: input) // BAD (missing anchor) + _ = try Regex("^a|(b)").firstMatch(in: input) // $ Alert[swift/missing-regexp-anchor] // BAD (missing anchor) _ = try Regex("^a|(^b)").firstMatch(in: input) - _ = try Regex("^(a)|(b)").firstMatch(in: input) // BAD (missing anchor) + _ = try Regex("^(a)|(b)").firstMatch(in: input) // $ Alert[swift/missing-regexp-anchor] // BAD (missing anchor) - _ = try Regex("a|b$").firstMatch(in: input) // BAD (missing anchor) + _ = try Regex("a|b$").firstMatch(in: input) // $ Alert[swift/missing-regexp-anchor] // BAD (missing anchor) _ = try Regex("a$|b").firstMatch(in: input) _ = try Regex("a$|b$").firstMatch(in: input) - _ = try Regex("a|b|c$").firstMatch(in: input) // BAD (missing anchor) + _ = try Regex("a|b|c$").firstMatch(in: input) // $ Alert[swift/missing-regexp-anchor] // BAD (missing anchor) _ = try Regex("a|b$|c").firstMatch(in: input) _ = try Regex("a$|b|c").firstMatch(in: input) _ = try Regex("a|b$|c$").firstMatch(in: input) _ = try Regex("a|(b$)").firstMatch(in: input) - _ = try Regex("(a)|b$").firstMatch(in: input) // BAD (missing anchor) + _ = try Regex("(a)|b$").firstMatch(in: input) // $ Alert[swift/missing-regexp-anchor] // BAD (missing anchor) _ = try Regex("(a$)|b$").firstMatch(in: input) - _ = try Regex("(a)|(b)$").firstMatch(in: input) // BAD (missing anchor) + _ = try Regex("(a)|(b)$").firstMatch(in: input) // $ Alert[swift/missing-regexp-anchor] // BAD (missing anchor) - _ = try Regex(#"^good.com|better.com"#).firstMatch(in: input) // BAD (missing anchor) - _ = try Regex(#"^good\.com|better\.com"#).firstMatch(in: input) // BAD (missing anchor) - _ = try Regex(#"^good\\.com|better\\.com"#).firstMatch(in: input) // BAD (missing anchor) - _ = try Regex(#"^good\\\.com|better\\\.com"#).firstMatch(in: input) // BAD (missing anchor) - _ = try Regex(#"^good\\\\.com|better\\\\.com"#).firstMatch(in: input) // BAD (missing anchor) + _ = try Regex(#"^good.com|better.com"#).firstMatch(in: input) // $ Alert[swift/missing-regexp-anchor] Alert[swift/missing-regexp-anchor] // BAD (missing anchor) + _ = try Regex(#"^good\.com|better\.com"#).firstMatch(in: input) // $ Alert[swift/missing-regexp-anchor] Alert[swift/missing-regexp-anchor] // BAD (missing anchor) + _ = try Regex(#"^good\\.com|better\\.com"#).firstMatch(in: input) // $ Alert[swift/missing-regexp-anchor] // BAD (missing anchor) + _ = try Regex(#"^good\\\.com|better\\\.com"#).firstMatch(in: input) // $ Alert[swift/missing-regexp-anchor] // BAD (missing anchor) + _ = try Regex(#"^good\\\\.com|better\\\\.com"#).firstMatch(in: input) // $ Alert[swift/missing-regexp-anchor] // BAD (missing anchor) - _ = try Regex("^foo|bar|baz$").firstMatch(in: input) // BAD (missing anchor) + _ = try Regex("^foo|bar|baz$").firstMatch(in: input) // $ Alert[swift/missing-regexp-anchor] Alert[swift/missing-regexp-anchor] // BAD (missing anchor) _ = try Regex("^foo|%").firstMatch(in: input) } func realWorld(input: String) throws { // real-world examples that have been anonymized a bit // the following are bad: - _ = try Regex(#"(\.xxx)|(\.yyy)|(\.zzz)$"#).firstMatch(in: input) // BAD (missing anchor) - _ = try Regex(#"(^left|right|center)\sbottom$"#).firstMatch(in: input) // BAD (missing anchor) - _ = try Regex(#"\.xxx|\.yyy|\.zzz$"#).ignoresCase().firstMatch(in: input) // BAD (missing anchor) - _ = try Regex(#"\.xxx|\.yyy|\.zzz$"#).ignoresCase().firstMatch(in: input) // BAD (missing anchor) - _ = try Regex(#"\.xxx|\.yyy|zzz$"#).firstMatch(in: input) // BAD (missing anchor) - _ = try Regex(#"^([A-Z]|xxx[XY]$)"#).firstMatch(in: input) // BAD (missing anchor) - _ = try Regex(#"^(xxx yyy zzz)|(xxx yyy)"#).ignoresCase().firstMatch(in: input) // BAD (missing anchor) - _ = try Regex(#"^(xxx yyy zzz)|(xxx yyy)|(1st( xxx)? yyy)|xxx|1st"#).ignoresCase().firstMatch(in: input) // BAD (missing anchor) - _ = try Regex(#"^(xxx:)|(yyy:)|(zzz:)"#).firstMatch(in: input) // BAD (missing anchor) - _ = try Regex(#"^(xxx?:)|(yyy:zzz\/)"#).firstMatch(in: input) // BAD (missing anchor) - _ = try Regex(#"^@media|@page"#).firstMatch(in: input) // BAD (missing anchor) - _ = try Regex(#"^\s*(xxx?|yyy|zzz):|xxx:yyy"#).firstMatch(in: input) // BAD (missing anchor) - _ = try Regex(#"^click|mouse|touch"#).firstMatch(in: input) // BAD (missing anchor) - _ = try Regex(#"^http://good\.com|http://better\.com"#).firstMatch(in: input) // BAD (missing anchor) - _ = try Regex(#"^https?://good\.com|https?://better\.com"#).firstMatch(in: input) // BAD (missing anchor) - _ = try Regex(#"^mouse|touch|click|contextmenu|drop|dragover|dragend"#).firstMatch(in: input) // BAD (missing anchor) - _ = try Regex(#"^xxx:|yyy:"#).ignoresCase().firstMatch(in: input) // BAD (missing anchor) - _ = try Regex(#"_xxx|_yyy|_zzz$"#).firstMatch(in: input) // BAD (missing anchor) - _ = try Regex(#"em|%$"#).firstMatch(in: input) // BAD (missing anchor) [NOT DETECTED] - not flagged at the moment due to the anchor not being for letters + _ = try Regex(#"(\.xxx)|(\.yyy)|(\.zzz)$"#).firstMatch(in: input) // $ Alert[swift/missing-regexp-anchor] // BAD (missing anchor) + _ = try Regex(#"(^left|right|center)\sbottom$"#).firstMatch(in: input) // $ Alert[swift/missing-regexp-anchor] // BAD (missing anchor) + _ = try Regex(#"\.xxx|\.yyy|\.zzz$"#).ignoresCase().firstMatch(in: input) // $ Alert[swift/missing-regexp-anchor] // BAD (missing anchor) + _ = try Regex(#"\.xxx|\.yyy|\.zzz$"#).ignoresCase().firstMatch(in: input) // $ Alert[swift/missing-regexp-anchor] // BAD (missing anchor) + _ = try Regex(#"\.xxx|\.yyy|zzz$"#).firstMatch(in: input) // $ Alert[swift/missing-regexp-anchor] // BAD (missing anchor) + _ = try Regex(#"^([A-Z]|xxx[XY]$)"#).firstMatch(in: input) // $ Alert[swift/missing-regexp-anchor] // BAD (missing anchor) + _ = try Regex(#"^(xxx yyy zzz)|(xxx yyy)"#).ignoresCase().firstMatch(in: input) // $ Alert[swift/missing-regexp-anchor] // BAD (missing anchor) + _ = try Regex(#"^(xxx yyy zzz)|(xxx yyy)|(1st( xxx)? yyy)|xxx|1st"#).ignoresCase().firstMatch(in: input) // $ Alert[swift/missing-regexp-anchor] // BAD (missing anchor) + _ = try Regex(#"^(xxx:)|(yyy:)|(zzz:)"#).firstMatch(in: input) // $ Alert[swift/missing-regexp-anchor] // BAD (missing anchor) + _ = try Regex(#"^(xxx?:)|(yyy:zzz\/)"#).firstMatch(in: input) // $ Alert[swift/missing-regexp-anchor] // BAD (missing anchor) + _ = try Regex(#"^@media|@page"#).firstMatch(in: input) // $ Alert[swift/missing-regexp-anchor] // BAD (missing anchor) + _ = try Regex(#"^\s*(xxx?|yyy|zzz):|xxx:yyy"#).firstMatch(in: input) // $ Alert[swift/missing-regexp-anchor] // BAD (missing anchor) + _ = try Regex(#"^click|mouse|touch"#).firstMatch(in: input) // $ Alert[swift/missing-regexp-anchor] // BAD (missing anchor) + _ = try Regex(#"^http://good\.com|http://better\.com"#).firstMatch(in: input) // $ Alert[swift/missing-regexp-anchor] Alert[swift/missing-regexp-anchor] // BAD (missing anchor) + _ = try Regex(#"^https?://good\.com|https?://better\.com"#).firstMatch(in: input) // $ Alert[swift/missing-regexp-anchor] Alert[swift/missing-regexp-anchor] // BAD (missing anchor) + _ = try Regex(#"^mouse|touch|click|contextmenu|drop|dragover|dragend"#).firstMatch(in: input) // $ Alert[swift/missing-regexp-anchor] // BAD (missing anchor) + _ = try Regex(#"^xxx:|yyy:"#).ignoresCase().firstMatch(in: input) // $ Alert[swift/missing-regexp-anchor] // BAD (missing anchor) + _ = try Regex(#"_xxx|_yyy|_zzz$"#).firstMatch(in: input) // $ Alert[swift/missing-regexp-anchor] // BAD (missing anchor) + _ = try Regex(#"em|%$"#).firstMatch(in: input) // $ MISSING: Alert[swift/missing-regexp-anchor] // BAD (missing anchor) [NOT DETECTED] - not flagged at the moment due to the anchor not being for letters // the following are MAYBE OK due to apparent complexity; not flagged _ = try Regex(#"(?:^[#?]?|&)([^=&]+)(?:=([^&]*))?"#).firstMatch(in: input) diff --git a/swift/ql/test/query-tests/Security/CWE-020/UnanchoredUrlRegex.swift b/swift/ql/test/query-tests/Security/CWE-020/UnanchoredUrlRegex.swift index b2e8810e7b7..683fc7213c3 100644 --- a/swift/ql/test/query-tests/Security/CWE-020/UnanchoredUrlRegex.swift +++ b/swift/ql/test/query-tests/Security/CWE-020/UnanchoredUrlRegex.swift @@ -59,36 +59,36 @@ func tests(url: String, secure: Bool) throws { let input = "http://evil.com/?http://good.com" let inputRange = NSMakeRange(0, input.utf16.count) - _ = try NSRegularExpression(pattern: "https?://good.com").matches(in: input, range: inputRange) // BAD (missing anchor) - _ = try NSRegularExpression(pattern: "https?://good.com").matches(in: input, range: inputRange) // BAD (missing anchor) - _ = try NSRegularExpression(pattern: "^https?://good.com").matches(in: input, range: inputRange) // BAD (missing post-anchor) - _ = try NSRegularExpression(pattern: "(^https?://good1.com)|(^https?://good2.com)").matches(in: input, range: inputRange) // BAD (missing post-anchor) - _ = try NSRegularExpression(pattern: "(https?://good.com)|(^https?://goodie.com)").matches(in: input, range: inputRange) // BAD (missing anchor) + _ = try NSRegularExpression(pattern: "https?://good.com").matches(in: input, range: inputRange) // $ Alert[swift/missing-regexp-anchor] // BAD (missing anchor) + _ = try NSRegularExpression(pattern: "https?://good.com").matches(in: input, range: inputRange) // $ Alert[swift/missing-regexp-anchor] // BAD (missing anchor) + _ = try NSRegularExpression(pattern: "^https?://good.com").matches(in: input, range: inputRange) // $ Alert[swift/missing-regexp-anchor] // BAD (missing post-anchor) + _ = try NSRegularExpression(pattern: "(^https?://good1.com)|(^https?://good2.com)").matches(in: input, range: inputRange) // $ Alert[swift/missing-regexp-anchor] // BAD (missing post-anchor) + _ = try NSRegularExpression(pattern: "(https?://good.com)|(^https?://goodie.com)").matches(in: input, range: inputRange) // $ Alert[swift/missing-regexp-anchor] Alert[swift/missing-regexp-anchor] // BAD (missing anchor) - _ = try NSRegularExpression(pattern: #"https?:\/\/good.com"#).matches(in: input, range: inputRange) // BAD (missing anchor) - _ = try NSRegularExpression(pattern: "https?://good.com").matches(in: input, range: inputRange) // BAD (missing anchor) + _ = try NSRegularExpression(pattern: #"https?:\/\/good.com"#).matches(in: input, range: inputRange) // $ Alert[swift/missing-regexp-anchor] // BAD (missing anchor) + _ = try NSRegularExpression(pattern: "https?://good.com").matches(in: input, range: inputRange) // $ Alert[swift/missing-regexp-anchor] // BAD (missing anchor) - if let _ = try NSRegularExpression(pattern: "https?://good.com").firstMatch(in: input, range: inputRange) { } // BAD (missing anchor) + if let _ = try NSRegularExpression(pattern: "https?://good.com").firstMatch(in: input, range: inputRange) { } // $ Alert[swift/missing-regexp-anchor] // BAD (missing anchor) let input2 = "something" let input2Range = NSMakeRange(0, input2.utf16.count) _ = try NSRegularExpression(pattern: "other").firstMatch(in: input2, range: input2Range) // OK _ = try NSRegularExpression(pattern: "x.commissary").firstMatch(in: input2, range: input2Range) // OK - _ = try NSRegularExpression(pattern: #"https?://good.com"#).firstMatch(in: input, range: inputRange) // BAD (missing anchor) - _ = try NSRegularExpression(pattern: #"https?://good.com:8080"#).firstMatch(in: input, range: inputRange) // BAD (missing anchor) + _ = try NSRegularExpression(pattern: #"https?://good.com"#).firstMatch(in: input, range: inputRange) // $ Alert[swift/missing-regexp-anchor] // BAD (missing anchor) + _ = try NSRegularExpression(pattern: #"https?://good.com:8080"#).firstMatch(in: input, range: inputRange) // $ Alert[swift/missing-regexp-anchor] // BAD (missing anchor) let trustedUrlRegexs = [ - "https?://good.com", // BAD (missing anchor), referenced below - #"https?:\/\/good.com"#, // BAD (missing anchor), referenced below - "^https?://good.com" // BAD (missing post-anchor), referenced below + "https?://good.com", // $ Alert[swift/missing-regexp-anchor] // BAD (missing anchor), referenced below + #"https?:\/\/good.com"#, // $ Alert[swift/missing-regexp-anchor] // BAD (missing anchor), referenced below + "^https?://good.com" // $ Alert[swift/missing-regexp-anchor] // BAD (missing post-anchor), referenced below ] for trustedUrlRegex in trustedUrlRegexs { if let _ = try NSRegularExpression(pattern: trustedUrlRegex).firstMatch(in: input, range: inputRange) { } } let trustedUrlRegexs2 = [ - "https?://good.com", // BAD (missing anchor), referenced below + "https?://good.com", // $ Alert[swift/missing-regexp-anchor] // BAD (missing anchor), referenced below ] if let _ = try NSRegularExpression(pattern: trustedUrlRegexs2[0]).firstMatch(in: input, range: inputRange) { } @@ -98,31 +98,31 @@ func tests(url: String, secure: Bool) throws { for _ in notUsedUrlRegexs { } - _ = try NSRegularExpression(pattern: #"https?:\/\/good.com\/([0-9]+)"#).matches(in: url, range: urlRange) // BAD (missing anchor) + _ = try NSRegularExpression(pattern: #"https?:\/\/good.com\/([0-9]+)"#).matches(in: url, range: urlRange) // $ Alert[swift/missing-regexp-anchor] // BAD (missing anchor) _ = try NSRegularExpression(pattern: "https://verygood.com/?id=" + #"https?:\/\/good.com\/([0-9]+)"#).matches(in: url, range: urlRange)[0] // OK _ = try NSRegularExpression(pattern: "http" + (secure ? "s" : "") + "://" + "verygood.com/?id=" + #"https?:\/\/good.com\/([0-9]+)"#).matches(in: url, range: urlRange)[0] // OK _ = try NSRegularExpression(pattern: "verygood.com/?id=" + #"https?:\/\/good.com\/([0-9]+)"#).matches(in: url, range: urlRange)[0] // OK _ = try NSRegularExpression(pattern: #"\.com|\.org"#).matches(in: input, range: inputRange) // OK, has no domain name - _ = try NSRegularExpression(pattern: #"example\.com|whatever"#).matches(in: input, range: inputRange) // OK, the other disjunction doesn't match a hostname [FALSE POSITIVE] + _ = try NSRegularExpression(pattern: #"example\.com|whatever"#).matches(in: input, range: inputRange) // $ SPURIOUS: Alert[swift/missing-regexp-anchor] // OK, the other disjunction doesn't match a hostname [FALSE POSITIVE] // tests for the `isLineAnchoredHostnameRegExp` case let attackUrl1 = "evil.com/blabla?\ngood.com" let attackUrl1Range = NSMakeRange(0, attackUrl1.utf16.count) _ = try NSRegularExpression(pattern: "^good\\.com$").matches(in: attackUrl1, range: attackUrl1Range) // OK - _ = try NSRegularExpression(pattern: "^good\\.com$", options: .anchorsMatchLines).matches(in: attackUrl1, range: attackUrl1Range) // BAD [NOT DETECTED]: with the .anchorsMatchLines option this matches the attack URL + _ = try NSRegularExpression(pattern: "^good\\.com$", options: .anchorsMatchLines).matches(in: attackUrl1, range: attackUrl1Range) // $ MISSING: Alert[swift/missing-regexp-anchor] // BAD [NOT DETECTED]: with the .anchorsMatchLines option this matches the attack URL _ = try NSRegularExpression(pattern: "(?i)^good\\.com$").matches(in: attackUrl1, range: attackUrl1Range) // OK - _ = try NSRegularExpression(pattern: "(?i)^good\\.com$", options: .anchorsMatchLines).matches(in: attackUrl1, range: attackUrl1Range) // BAD [NOT DETECTED]: with the .anchorsMatchLines option this matches the attack URL + _ = try NSRegularExpression(pattern: "(?i)^good\\.com$", options: .anchorsMatchLines).matches(in: attackUrl1, range: attackUrl1Range) // $ MISSING: Alert[swift/missing-regexp-anchor] // BAD [NOT DETECTED]: with the .anchorsMatchLines option this matches the attack URL _ = try NSRegularExpression(pattern: "^good\\.com$|^another\\.com$").matches(in: attackUrl1, range: attackUrl1Range) // OK - _ = try NSRegularExpression(pattern: "^good\\.com$|^another\\.com$", options: .anchorsMatchLines).matches(in: attackUrl1, range: attackUrl1Range) // BAD [NOT DETECTED]: with the .anchorsMatchLines option this matches the attack URL + _ = try NSRegularExpression(pattern: "^good\\.com$|^another\\.com$", options: .anchorsMatchLines).matches(in: attackUrl1, range: attackUrl1Range) // $ MISSING: Alert[swift/missing-regexp-anchor] // BAD [NOT DETECTED]: with the .anchorsMatchLines option this matches the attack URL let attackUrl2 = "evil.com/blabla?\ngood.com/" let attackUrl2Range = NSMakeRange(0, attackUrl2.utf16.count) _ = try NSRegularExpression(pattern: "^good\\.com/").matches(in: attackUrl2, range: attackUrl2Range) // OK - _ = try NSRegularExpression(pattern: "^good\\.com/", options: .anchorsMatchLines).matches(in: attackUrl2, range: attackUrl2Range) // BAD [NOT DETECTED]: with the .anchorsMatchLines option this matches the attack URL + _ = try NSRegularExpression(pattern: "^good\\.com/", options: .anchorsMatchLines).matches(in: attackUrl2, range: attackUrl2Range) // $ MISSING: Alert[swift/missing-regexp-anchor] // BAD [NOT DETECTED]: with the .anchorsMatchLines option this matches the attack URL _ = try NSRegularExpression(pattern: "(?i)^good\\.com/").matches(in: attackUrl2, range: attackUrl2Range) // OK - _ = try NSRegularExpression(pattern: "(?i)^good\\.com/", options: .anchorsMatchLines).matches(in: attackUrl2, range: attackUrl2Range) // BAD [NOT DETECTED]: with the .anchorsMatchLines option this matches the attack URL + _ = try NSRegularExpression(pattern: "(?i)^good\\.com/", options: .anchorsMatchLines).matches(in: attackUrl2, range: attackUrl2Range) // $ MISSING: Alert[swift/missing-regexp-anchor] // BAD [NOT DETECTED]: with the .anchorsMatchLines option this matches the attack URL _ = try NSRegularExpression(pattern: "^good\\.com/|^another\\.com/").matches(in: attackUrl2, range: attackUrl2Range) // OK - _ = try NSRegularExpression(pattern: "^good\\.com/|^another\\.com/", options: .anchorsMatchLines).matches(in: attackUrl2, range: attackUrl2Range) // BAD [NOT DETECTED]: with the .anchorsMatchLines option this matches the attack URL + _ = try NSRegularExpression(pattern: "^good\\.com/|^another\\.com/", options: .anchorsMatchLines).matches(in: attackUrl2, range: attackUrl2Range) // $ MISSING: Alert[swift/missing-regexp-anchor] // BAD [NOT DETECTED]: with the .anchorsMatchLines option this matches the attack URL } diff --git a/swift/ql/test/query-tests/Security/CWE-020/test.swift b/swift/ql/test/query-tests/Security/CWE-020/test.swift index e19af9050fd..384d5875476 100644 --- a/swift/ql/test/query-tests/Security/CWE-020/test.swift +++ b/swift/ql/test/query-tests/Security/CWE-020/test.swift @@ -53,61 +53,61 @@ func testHostnames(myUrl: URL) throws { _ = try Regex(#"^http://example\.com/"#).firstMatch(in: tainted) // GOOD _ = try Regex(#"^http://example.com/"#).firstMatch(in: tainted) // GOOD (only '.' here gives a valid top-level domain) - _ = try Regex(#"^http://example.com"#).firstMatch(in: tainted) // BAD (missing anchor) + _ = try Regex(#"^http://example.com"#).firstMatch(in: tainted) // $ Alert[swift/missing-regexp-anchor] // BAD (missing anchor) _ = try Regex(#"^http://test\.example\.com/"#).firstMatch(in: tainted) // GOOD _ = try Regex(#"^http://test\.example.com/"#).firstMatch(in: tainted) // GOOD (only '.' here gives a valid top-level domain) - _ = try Regex(#"^http://test\.example.com"#).firstMatch(in: tainted) // BAD (missing anchor) - _ = try Regex(#"^http://test.example.com/"#).firstMatch(in: tainted) // BAD (incomplete hostname) + _ = try Regex(#"^http://test\.example.com"#).firstMatch(in: tainted) // $ Alert[swift/missing-regexp-anchor] // BAD (missing anchor) + _ = try Regex(#"^http://test.example.com/"#).firstMatch(in: tainted) // $ Alert[swift/incomplete-hostname-regexp] // BAD (incomplete hostname) _ = try Regex(#"^http://test[.]example[.]com/"#).firstMatch(in: tainted) // GOOD (alternative method of escaping) - _ = try Regex(#"^http://test.example.net/"#).firstMatch(in: tainted) // BAD (incomplete hostname) - _ = try Regex(#"^http://test.(example-a|example-b).com/"#).firstMatch(in: tainted) // BAD (incomplete hostname) - _ = try Regex(#"^http://(.+).example.com/"#).firstMatch(in: tainted) // BAD (incomplete hostname x 2) + _ = try Regex(#"^http://test.example.net/"#).firstMatch(in: tainted) // $ Alert[swift/incomplete-hostname-regexp] // BAD (incomplete hostname) + _ = try Regex(#"^http://test.(example-a|example-b).com/"#).firstMatch(in: tainted) // $ Alert[swift/incomplete-hostname-regexp] // BAD (incomplete hostname) + _ = try Regex(#"^http://(.+).example.com/"#).firstMatch(in: tainted) // $ Alert[swift/incomplete-hostname-regexp] Alert[swift/incomplete-hostname-regexp] // BAD (incomplete hostname x 2) _ = try Regex(#"^http://(\.+)\.example.com/"#).firstMatch(in: tainted) // GOOD - _ = try Regex(#"^http://(?:.+)\.test\.example.com/"#).firstMatch(in: tainted) // BAD (incomplete hostname) - _ = try Regex(#"^http://test.example.com/(?:.*)"#).firstMatch(in: tainted) // BAD (incomplete hostname) - _ = try Regex(#"^(.+\.(?:example-a|example-b)\.com)/"#).firstMatch(in: tainted) // BAD (missing anchor) - _ = try Regex(#"^(https?:)?//((service|www).)?example.com(?=$|/)"#).firstMatch(in: tainted) // BAD (incomplete hostname) - _ = try Regex(#"^(http|https)://www.example.com/p/f/"#).firstMatch(in: tainted) // BAD (incomplete hostname) - _ = try Regex(#"^(http://sub.example.com/)"#).firstMatch(in: tainted) // BAD (incomplete hostname) - _ = try Regex(#"^https?://api.example.com/"#).firstMatch(in: tainted) // BAD (incomplete hostname) + _ = try Regex(#"^http://(?:.+)\.test\.example.com/"#).firstMatch(in: tainted) // $ Alert[swift/incomplete-hostname-regexp] // BAD (incomplete hostname) + _ = try Regex(#"^http://test.example.com/(?:.*)"#).firstMatch(in: tainted) // $ Alert[swift/incomplete-hostname-regexp] // BAD (incomplete hostname) + _ = try Regex(#"^(.+\.(?:example-a|example-b)\.com)/"#).firstMatch(in: tainted) // $ Alert[swift/missing-regexp-anchor] // BAD (missing anchor) + _ = try Regex(#"^(https?:)?//((service|www).)?example.com(?=$|/)"#).firstMatch(in: tainted) // $ Alert[swift/incomplete-hostname-regexp] // BAD (incomplete hostname) + _ = try Regex(#"^(http|https)://www.example.com/p/f/"#).firstMatch(in: tainted) // $ Alert[swift/incomplete-hostname-regexp] // BAD (incomplete hostname) + _ = try Regex(#"^(http://sub.example.com/)"#).firstMatch(in: tainted) // $ Alert[swift/incomplete-hostname-regexp] // BAD (incomplete hostname) + _ = try Regex(#"^https?://api.example.com/"#).firstMatch(in: tainted) // $ Alert[swift/incomplete-hostname-regexp] // BAD (incomplete hostname) _ = try Regex(#"^http[s]?://?sub1\.sub2\.example\.com/f/(.+)"#).firstMatch(in: tainted) // GOOD (it has a capture group after the TLD, so should be ignored) - _ = try Regex(#"^https://[a-z]*.example.com$"#).firstMatch(in: tainted) // BAD (incomplete hostname) - _ = try Regex(#"^(example.dev|example.com)"#).firstMatch(in: tainted) // GOOD (any extended hostname wouldn't be included in the capture group) [FALSE POSITIVE] - _ = try Regex(#"^protos?://(localhost|.+.example.net|.+.example-a.com|.+.example-b.com|.+.example.internal)"#).firstMatch(in: tainted) // BAD (incomplete hostname x3, missing anchor x 1) + _ = try Regex(#"^https://[a-z]*.example.com$"#).firstMatch(in: tainted) // $ Alert[swift/incomplete-hostname-regexp] // BAD (incomplete hostname) + _ = try Regex(#"^(example.dev|example.com)"#).firstMatch(in: tainted) // $ SPURIOUS: Alert[swift/missing-regexp-anchor] // GOOD (any extended hostname wouldn't be included in the capture group) [FALSE POSITIVE] + _ = try Regex(#"^protos?://(localhost|.+.example.net|.+.example-a.com|.+.example-b.com|.+.example.internal)"#).firstMatch(in: tainted) // $ Alert[swift/incomplete-hostname-regexp] Alert[swift/missing-regexp-anchor] // BAD (incomplete hostname x3, missing anchor x 1) _ = try Regex(#"^http://(..|...)\.example\.com/index\.html"#).firstMatch(in: tainted) // GOOD (wildcards are intentional) _ = try Regex(#"^http://.\.example\.com/index\.html"#).firstMatch(in: tainted) // GOOD (the wildcard is intentional) - _ = try Regex(#"^(foo.example\.com|whatever)$"#).firstMatch(in: tainted) // DUBIOUS (one disjunction doesn't even look like a hostname) [DETECTED incomplete hostname, missing anchor] + _ = try Regex(#"^(foo.example\.com|whatever)$"#).firstMatch(in: tainted) // $ Alert // DUBIOUS (one disjunction doesn't even look like a hostname) [DETECTED incomplete hostname, missing anchor] - _ = try Regex(#"^test.example.com$"#).firstMatch(in: tainted) // BAD (incomplete hostname) - _ = try Regex(#"test.example.com"#).wholeMatch(in: tainted) // BAD (incomplete hostname, missing anchor) + _ = try Regex(#"^test.example.com$"#).firstMatch(in: tainted) // $ Alert[swift/incomplete-hostname-regexp] // BAD (incomplete hostname) + _ = try Regex(#"test.example.com"#).wholeMatch(in: tainted) // $ Alert // BAD (incomplete hostname, missing anchor) - _ = try Regex(id(id(id(#"test.example.com$"#)))).firstMatch(in: tainted) // BAD (incomplete hostname) + _ = try Regex(id(id(id(#"test.example.com$"#)))).firstMatch(in: tainted) // $ Alert[swift/incomplete-hostname-regexp] // BAD (incomplete hostname) - let hostname = #"test.example.com$"# // BAD (incomplete hostname) [NOT DETECTED] + let hostname = #"test.example.com$"# // $ MISSING: Alert[swift/incomplete-hostname-regexp] // BAD (incomplete hostname) [NOT DETECTED] _ = try Regex("\(hostname)").firstMatch(in: tainted) var domain = MyDomain("") - domain.hostname = #"test.example.com$"# // BAD (incomplete hostname) + domain.hostname = #"test.example.com$"# // $ Alert[swift/incomplete-hostname-regexp] // BAD (incomplete hostname) _ = try Regex(domain.hostname).firstMatch(in: tainted) func convert1(_ domain: MyDomain) throws -> Regex { return try Regex(domain.hostname) } - _ = try convert1(MyDomain(#"test.example.com$"#)).firstMatch(in: tainted) // BAD (incomplete hostname) + _ = try convert1(MyDomain(#"test.example.com$"#)).firstMatch(in: tainted) // $ Alert[swift/incomplete-hostname-regexp] // BAD (incomplete hostname) - let domains = [ MyDomain(#"test.example.com$"#) ] // BAD (incomplete hostname) [NOT DETECTED] + let domains = [ MyDomain(#"test.example.com$"#) ] // $ MISSING: Alert[swift/incomplete-hostname-regexp] // BAD (incomplete hostname) [NOT DETECTED] func convert2(_ domain: MyDomain) throws -> Regex { return try Regex(domain.hostname) } _ = try domains.map({ try convert2($0).firstMatch(in: tainted) }) let primary = "example.com$" - _ = try Regex("test." + primary).firstMatch(in: tainted) // BAD (incomplete hostname) [NOT DETECTED] - _ = try Regex("test." + "example.com$").firstMatch(in: tainted) // BAD (incomplete hostname) [NOT DETECTED] - _ = try Regex(#"^http://localhost:8000|" + "^https?://.+\.example\.com/"#).firstMatch(in: tainted) // BAD (incomplete hostname) [NOT DETECTED] - _ = try Regex(#"^http://localhost:8000|" + "^https?://.+.example\.com/"#).firstMatch(in: tainted) // BAD (incomplete hostname) [NOT DETECTED] + _ = try Regex("test." + primary).firstMatch(in: tainted) // $ MISSING: Alert[swift/incomplete-hostname-regexp] // BAD (incomplete hostname) [NOT DETECTED] + _ = try Regex("test." + "example.com$").firstMatch(in: tainted) // $ MISSING: Alert[swift/incomplete-hostname-regexp] // BAD (incomplete hostname) [NOT DETECTED] + _ = try Regex(#"^http://localhost:8000|" + "^https?://.+\.example\.com/"#).firstMatch(in: tainted) // $ MISSING: Alert[swift/incomplete-hostname-regexp] // BAD (incomplete hostname) [NOT DETECTED] + _ = try Regex(#"^http://localhost:8000|" + "^https?://.+.example\.com/"#).firstMatch(in: tainted) // $ MISSING: Alert[swift/incomplete-hostname-regexp] // BAD (incomplete hostname) [NOT DETECTED] let harmless = #"^http://test.example.com"# // GOOD (never used as a regex) } diff --git a/swift/ql/test/query-tests/Security/CWE-022/UnsafeUnpack/UnsafeUnpack.qlref b/swift/ql/test/query-tests/Security/CWE-022/UnsafeUnpack/UnsafeUnpack.qlref index 1d1a5a3a84c..f637622e3a1 100644 --- a/swift/ql/test/query-tests/Security/CWE-022/UnsafeUnpack/UnsafeUnpack.qlref +++ b/swift/ql/test/query-tests/Security/CWE-022/UnsafeUnpack/UnsafeUnpack.qlref @@ -1 +1,2 @@ -experimental/Security/CWE-022/UnsafeUnpack.ql \ No newline at end of file +query: experimental/Security/CWE-022/UnsafeUnpack.ql +postprocess: utils/test/InlineExpectationsTestQuery.ql diff --git a/swift/ql/test/query-tests/Security/CWE-022/UnsafeUnpack/UnsafeUnpack.swift b/swift/ql/test/query-tests/Security/CWE-022/UnsafeUnpack/UnsafeUnpack.swift index 5d7dc6c58b4..0f6a7cc8b28 100644 --- a/swift/ql/test/query-tests/Security/CWE-022/UnsafeUnpack/UnsafeUnpack.swift +++ b/swift/ql/test/query-tests/Security/CWE-022/UnsafeUnpack/UnsafeUnpack.swift @@ -59,12 +59,12 @@ func testCommandInjectionQhelpExamples() { let source = URL(fileURLWithPath: "/sourcePath") let destination = URL(fileURLWithPath: "/destination") - try Data(contentsOf: remoteURL, options: []).write(to: source) + try Data(contentsOf: remoteURL, options: []).write(to: source) // $ Source do { - try Zip.unzipFile(source, destination: destination, overwrite: true, password: nil) // BAD + try Zip.unzipFile(source, destination: destination, overwrite: true, password: nil) // $ Alert let fileManager = FileManager() - try fileManager.unzipItem(at: source, to: destination) // BAD + try fileManager.unzipItem(at: source, to: destination) // $ Alert } catch { print("Error: \(error)") } diff --git a/swift/ql/test/query-tests/Security/CWE-079/UnsafeWebViewFetch.expected b/swift/ql/test/query-tests/Security/CWE-079/UnsafeWebViewFetch.expected index c2fefc171e6..d796aa2da25 100644 --- a/swift/ql/test/query-tests/Security/CWE-079/UnsafeWebViewFetch.expected +++ b/swift/ql/test/query-tests/Security/CWE-079/UnsafeWebViewFetch.expected @@ -1,3 +1,22 @@ +#select +| UnsafeWebViewFetch.swift:103:25:103:84 | try! ... | UnsafeWebViewFetch.swift:103:30:103:84 | call to String.init(contentsOf:) | UnsafeWebViewFetch.swift:103:25:103:84 | try! ... | Tainted data is used in a WebView fetch without restricting the base URL. | +| UnsafeWebViewFetch.swift:106:25:106:25 | data | UnsafeWebViewFetch.swift:105:18:105:72 | call to String.init(contentsOf:) | UnsafeWebViewFetch.swift:106:25:106:25 | data | Tainted data is used in a WebView fetch without restricting the base URL. | +| UnsafeWebViewFetch.swift:109:25:109:53 | try! ... | UnsafeWebViewFetch.swift:109:30:109:53 | call to String.init(contentsOf:) | UnsafeWebViewFetch.swift:109:25:109:53 | try! ... | Tainted data is used in a WebView fetch without restricting the base URL. | +| UnsafeWebViewFetch.swift:120:25:120:39 | call to getRemoteData() | UnsafeWebViewFetch.swift:94:14:94:37 | call to String.init(contentsOf:) | UnsafeWebViewFetch.swift:120:25:120:39 | call to getRemoteData() | Tainted data is used in a WebView fetch without restricting the base URL. | +| UnsafeWebViewFetch.swift:121:25:121:25 | remoteString | UnsafeWebViewFetch.swift:94:14:94:37 | call to String.init(contentsOf:) | UnsafeWebViewFetch.swift:121:25:121:25 | remoteString | Tainted data is used in a WebView fetch without restricting the base URL. | +| UnsafeWebViewFetch.swift:124:25:124:51 | ... .+(_:_:) ... | UnsafeWebViewFetch.swift:94:14:94:37 | call to String.init(contentsOf:) | UnsafeWebViewFetch.swift:124:25:124:51 | ... .+(_:_:) ... | Tainted data is used in a WebView fetch without restricting the base URL. | +| UnsafeWebViewFetch.swift:127:25:127:25 | "..." | UnsafeWebViewFetch.swift:94:14:94:37 | call to String.init(contentsOf:) | UnsafeWebViewFetch.swift:127:25:127:25 | "..." | Tainted data is used in a WebView fetch without restricting the base URL. | +| UnsafeWebViewFetch.swift:139:25:139:25 | remoteString | UnsafeWebViewFetch.swift:94:14:94:37 | call to String.init(contentsOf:) | UnsafeWebViewFetch.swift:139:25:139:25 | remoteString | Tainted data is used in a WebView fetch with a tainted base URL. | +| UnsafeWebViewFetch.swift:141:25:141:25 | remoteString | UnsafeWebViewFetch.swift:94:14:94:37 | call to String.init(contentsOf:) | UnsafeWebViewFetch.swift:141:25:141:25 | remoteString | Tainted data is used in a WebView fetch with a tainted base URL. | +| UnsafeWebViewFetch.swift:154:15:154:15 | remoteData | UnsafeWebViewFetch.swift:94:14:94:37 | call to String.init(contentsOf:) | UnsafeWebViewFetch.swift:154:15:154:15 | remoteData | Tainted data is used in a WebView fetch with a tainted base URL. | +| UnsafeWebViewFetch.swift:167:25:167:39 | call to getRemoteData() | UnsafeWebViewFetch.swift:94:14:94:37 | call to String.init(contentsOf:) | UnsafeWebViewFetch.swift:167:25:167:39 | call to getRemoteData() | Tainted data is used in a WebView fetch without restricting the base URL. | +| UnsafeWebViewFetch.swift:168:25:168:25 | remoteString | UnsafeWebViewFetch.swift:94:14:94:37 | call to String.init(contentsOf:) | UnsafeWebViewFetch.swift:168:25:168:25 | remoteString | Tainted data is used in a WebView fetch without restricting the base URL. | +| UnsafeWebViewFetch.swift:171:25:171:51 | ... .+(_:_:) ... | UnsafeWebViewFetch.swift:94:14:94:37 | call to String.init(contentsOf:) | UnsafeWebViewFetch.swift:171:25:171:51 | ... .+(_:_:) ... | Tainted data is used in a WebView fetch without restricting the base URL. | +| UnsafeWebViewFetch.swift:174:25:174:25 | "..." | UnsafeWebViewFetch.swift:94:14:94:37 | call to String.init(contentsOf:) | UnsafeWebViewFetch.swift:174:25:174:25 | "..." | Tainted data is used in a WebView fetch without restricting the base URL. | +| UnsafeWebViewFetch.swift:186:25:186:25 | remoteString | UnsafeWebViewFetch.swift:94:14:94:37 | call to String.init(contentsOf:) | UnsafeWebViewFetch.swift:186:25:186:25 | remoteString | Tainted data is used in a WebView fetch with a tainted base URL. | +| UnsafeWebViewFetch.swift:188:25:188:25 | remoteString | UnsafeWebViewFetch.swift:94:14:94:37 | call to String.init(contentsOf:) | UnsafeWebViewFetch.swift:188:25:188:25 | remoteString | Tainted data is used in a WebView fetch with a tainted base URL. | +| UnsafeWebViewFetch.swift:201:15:201:15 | remoteData | UnsafeWebViewFetch.swift:94:14:94:37 | call to String.init(contentsOf:) | UnsafeWebViewFetch.swift:201:15:201:15 | remoteData | Tainted data is used in a WebView fetch with a tainted base URL. | +| UnsafeWebViewFetch.swift:210:25:210:25 | htmlData | UnsafeWebViewFetch.swift:94:14:94:37 | call to String.init(contentsOf:) | UnsafeWebViewFetch.swift:210:25:210:25 | htmlData | Tainted data is used in a WebView fetch without restricting the base URL. | edges | UnsafeWebViewFetch.swift:94:10:94:37 | try ... | UnsafeWebViewFetch.swift:117:21:117:35 | call to getRemoteData() | provenance | | | UnsafeWebViewFetch.swift:94:10:94:37 | try ... | UnsafeWebViewFetch.swift:120:25:120:39 | call to getRemoteData() | provenance | | @@ -135,22 +154,3 @@ nodes | UnsafeWebViewFetch.swift:210:25:210:25 | htmlData | semmle.label | htmlData | | UnsafeWebViewFetch.swift:211:25:211:25 | htmlData | semmle.label | htmlData | subpaths -#select -| UnsafeWebViewFetch.swift:103:25:103:84 | try! ... | UnsafeWebViewFetch.swift:103:30:103:84 | call to String.init(contentsOf:) | UnsafeWebViewFetch.swift:103:25:103:84 | try! ... | Tainted data is used in a WebView fetch without restricting the base URL. | -| UnsafeWebViewFetch.swift:106:25:106:25 | data | UnsafeWebViewFetch.swift:105:18:105:72 | call to String.init(contentsOf:) | UnsafeWebViewFetch.swift:106:25:106:25 | data | Tainted data is used in a WebView fetch without restricting the base URL. | -| UnsafeWebViewFetch.swift:109:25:109:53 | try! ... | UnsafeWebViewFetch.swift:109:30:109:53 | call to String.init(contentsOf:) | UnsafeWebViewFetch.swift:109:25:109:53 | try! ... | Tainted data is used in a WebView fetch without restricting the base URL. | -| UnsafeWebViewFetch.swift:120:25:120:39 | call to getRemoteData() | UnsafeWebViewFetch.swift:94:14:94:37 | call to String.init(contentsOf:) | UnsafeWebViewFetch.swift:120:25:120:39 | call to getRemoteData() | Tainted data is used in a WebView fetch without restricting the base URL. | -| UnsafeWebViewFetch.swift:121:25:121:25 | remoteString | UnsafeWebViewFetch.swift:94:14:94:37 | call to String.init(contentsOf:) | UnsafeWebViewFetch.swift:121:25:121:25 | remoteString | Tainted data is used in a WebView fetch without restricting the base URL. | -| UnsafeWebViewFetch.swift:124:25:124:51 | ... .+(_:_:) ... | UnsafeWebViewFetch.swift:94:14:94:37 | call to String.init(contentsOf:) | UnsafeWebViewFetch.swift:124:25:124:51 | ... .+(_:_:) ... | Tainted data is used in a WebView fetch without restricting the base URL. | -| UnsafeWebViewFetch.swift:127:25:127:25 | "..." | UnsafeWebViewFetch.swift:94:14:94:37 | call to String.init(contentsOf:) | UnsafeWebViewFetch.swift:127:25:127:25 | "..." | Tainted data is used in a WebView fetch without restricting the base URL. | -| UnsafeWebViewFetch.swift:139:25:139:25 | remoteString | UnsafeWebViewFetch.swift:94:14:94:37 | call to String.init(contentsOf:) | UnsafeWebViewFetch.swift:139:25:139:25 | remoteString | Tainted data is used in a WebView fetch with a tainted base URL. | -| UnsafeWebViewFetch.swift:141:25:141:25 | remoteString | UnsafeWebViewFetch.swift:94:14:94:37 | call to String.init(contentsOf:) | UnsafeWebViewFetch.swift:141:25:141:25 | remoteString | Tainted data is used in a WebView fetch with a tainted base URL. | -| UnsafeWebViewFetch.swift:154:15:154:15 | remoteData | UnsafeWebViewFetch.swift:94:14:94:37 | call to String.init(contentsOf:) | UnsafeWebViewFetch.swift:154:15:154:15 | remoteData | Tainted data is used in a WebView fetch with a tainted base URL. | -| UnsafeWebViewFetch.swift:167:25:167:39 | call to getRemoteData() | UnsafeWebViewFetch.swift:94:14:94:37 | call to String.init(contentsOf:) | UnsafeWebViewFetch.swift:167:25:167:39 | call to getRemoteData() | Tainted data is used in a WebView fetch without restricting the base URL. | -| UnsafeWebViewFetch.swift:168:25:168:25 | remoteString | UnsafeWebViewFetch.swift:94:14:94:37 | call to String.init(contentsOf:) | UnsafeWebViewFetch.swift:168:25:168:25 | remoteString | Tainted data is used in a WebView fetch without restricting the base URL. | -| UnsafeWebViewFetch.swift:171:25:171:51 | ... .+(_:_:) ... | UnsafeWebViewFetch.swift:94:14:94:37 | call to String.init(contentsOf:) | UnsafeWebViewFetch.swift:171:25:171:51 | ... .+(_:_:) ... | Tainted data is used in a WebView fetch without restricting the base URL. | -| UnsafeWebViewFetch.swift:174:25:174:25 | "..." | UnsafeWebViewFetch.swift:94:14:94:37 | call to String.init(contentsOf:) | UnsafeWebViewFetch.swift:174:25:174:25 | "..." | Tainted data is used in a WebView fetch without restricting the base URL. | -| UnsafeWebViewFetch.swift:186:25:186:25 | remoteString | UnsafeWebViewFetch.swift:94:14:94:37 | call to String.init(contentsOf:) | UnsafeWebViewFetch.swift:186:25:186:25 | remoteString | Tainted data is used in a WebView fetch with a tainted base URL. | -| UnsafeWebViewFetch.swift:188:25:188:25 | remoteString | UnsafeWebViewFetch.swift:94:14:94:37 | call to String.init(contentsOf:) | UnsafeWebViewFetch.swift:188:25:188:25 | remoteString | Tainted data is used in a WebView fetch with a tainted base URL. | -| UnsafeWebViewFetch.swift:201:15:201:15 | remoteData | UnsafeWebViewFetch.swift:94:14:94:37 | call to String.init(contentsOf:) | UnsafeWebViewFetch.swift:201:15:201:15 | remoteData | Tainted data is used in a WebView fetch with a tainted base URL. | -| UnsafeWebViewFetch.swift:210:25:210:25 | htmlData | UnsafeWebViewFetch.swift:94:14:94:37 | call to String.init(contentsOf:) | UnsafeWebViewFetch.swift:210:25:210:25 | htmlData | Tainted data is used in a WebView fetch without restricting the base URL. | diff --git a/swift/ql/test/query-tests/Security/CWE-079/UnsafeWebViewFetch.qlref b/swift/ql/test/query-tests/Security/CWE-079/UnsafeWebViewFetch.qlref index a5c8cb457a0..18d2fc0a49d 100644 --- a/swift/ql/test/query-tests/Security/CWE-079/UnsafeWebViewFetch.qlref +++ b/swift/ql/test/query-tests/Security/CWE-079/UnsafeWebViewFetch.qlref @@ -1 +1,2 @@ -queries/Security/CWE-079/UnsafeWebViewFetch.ql \ No newline at end of file +query: queries/Security/CWE-079/UnsafeWebViewFetch.ql +postprocess: utils/test/InlineExpectationsTestQuery.ql diff --git a/swift/ql/test/query-tests/Security/CWE-079/UnsafeWebViewFetch.swift b/swift/ql/test/query-tests/Security/CWE-079/UnsafeWebViewFetch.swift index 1b687ade014..cba21bcc455 100644 --- a/swift/ql/test/query-tests/Security/CWE-079/UnsafeWebViewFetch.swift +++ b/swift/ql/test/query-tests/Security/CWE-079/UnsafeWebViewFetch.swift @@ -91,7 +91,7 @@ func getRemoteData() -> String { let url = URL(string: "http://example.com/") do { - return try String(contentsOf: url!) + return try String(contentsOf: url!) // $ Source } catch { return "" } @@ -100,13 +100,13 @@ func getRemoteData() -> String { func testSimpleFlows() { let webview = UIWebView() - webview.loadHTMLString(try! String(contentsOf: URL(string: "http://example.com/")!), baseURL: nil) // BAD + webview.loadHTMLString(try! String(contentsOf: URL(string: "http://example.com/")!), baseURL: nil) // $ Alert - let data = try! String(contentsOf: URL(string: "http://example.com/")!) - webview.loadHTMLString(data, baseURL: nil) // BAD + let data = try! String(contentsOf: URL(string: "http://example.com/")!) // $ Source + webview.loadHTMLString(data, baseURL: nil) // $ Alert let url = URL(string: "http://example.com/") - webview.loadHTMLString(try! String(contentsOf: url!), baseURL: nil) // BAD + webview.loadHTMLString(try! String(contentsOf: url!), baseURL: nil) // $ Alert } func testUIWebView() { @@ -117,14 +117,14 @@ func testUIWebView() { let remoteString = getRemoteData() webview.loadHTMLString(localString, baseURL: nil) // GOOD: the HTML data is local - webview.loadHTMLString(getRemoteData(), baseURL: nil) // BAD: HTML contains remote input, may access local secrets - webview.loadHTMLString(remoteString, baseURL: nil) // BAD + webview.loadHTMLString(getRemoteData(), baseURL: nil) // $ Alert // BAD: HTML contains remote input, may access local secrets + webview.loadHTMLString(remoteString, baseURL: nil) // $ Alert webview.loadHTMLString("" + localStringFragment + "", baseURL: nil) // GOOD: the HTML data is local - webview.loadHTMLString("" + remoteString + "", baseURL: nil) // BAD + webview.loadHTMLString("" + remoteString + "", baseURL: nil) // $ Alert webview.loadHTMLString("\(localStringFragment)", baseURL: nil) // GOOD: the HTML data is local - webview.loadHTMLString("\(remoteString)", baseURL: nil) // BAD + webview.loadHTMLString("\(remoteString)", baseURL: nil) // $ Alert let localSafeURL = URL(string: "about:blank") let localURL = URL(string: "http://example.com/") @@ -136,9 +136,9 @@ func testUIWebView() { webview.loadHTMLString(localString, baseURL: localURL!) // GOOD: a presumed safe baseURL is specified webview.loadHTMLString(remoteString, baseURL: localURL!) // GOOD: a presumed safe baseURL is specified webview.loadHTMLString(localString, baseURL: remoteURL!) // GOOD: the HTML data is local - webview.loadHTMLString(remoteString, baseURL: remoteURL!) // BAD + webview.loadHTMLString(remoteString, baseURL: remoteURL!) // $ Alert webview.loadHTMLString(localString, baseURL: remoteURL2!) // GOOD: the HTML data is local - webview.loadHTMLString(remoteString, baseURL: remoteURL2!) // BAD + webview.loadHTMLString(remoteString, baseURL: remoteURL2!) // $ Alert let localRequest = URLRequest(url: localURL!) let remoteRequest = URLRequest(url: remoteURL!) @@ -151,7 +151,7 @@ func testUIWebView() { webview.load(localData, mimeType: "text/html", textEncodingName: "utf-8", baseURL: localSafeURL!) // GOOD: the data is local webview.load(remoteData, mimeType: "text/html", textEncodingName: "utf-8", baseURL: localSafeURL!) // GOOD: a safe baseURL is specified webview.load(localData, mimeType: "text/html", textEncodingName: "utf-8", baseURL: remoteURL!) // GOOD: the HTML data is local - webview.load(remoteData, mimeType: "text/html", textEncodingName: "utf-8", baseURL: remoteURL!) // BAD + webview.load(remoteData, mimeType: "text/html", textEncodingName: "utf-8", baseURL: remoteURL!) // $ Alert } func testWKWebView() { @@ -164,14 +164,14 @@ func testWKWebView() { let remoteString = getRemoteData() webview.loadHTMLString(localString, baseURL: nil) // GOOD: the HTML data is local - webview.loadHTMLString(getRemoteData(), baseURL: nil) // BAD - webview.loadHTMLString(remoteString, baseURL: nil) // BAD + webview.loadHTMLString(getRemoteData(), baseURL: nil) // $ Alert + webview.loadHTMLString(remoteString, baseURL: nil) // $ Alert webview.loadHTMLString("" + localStringFragment + "", baseURL: nil) // GOOD: the HTML data is local - webview.loadHTMLString("" + remoteString + "", baseURL: nil) // BAD + webview.loadHTMLString("" + remoteString + "", baseURL: nil) // $ Alert webview.loadHTMLString("\(localStringFragment)", baseURL: nil) // GOOD: the HTML data is local - webview.loadHTMLString("\(remoteString)", baseURL: nil) // BAD + webview.loadHTMLString("\(remoteString)", baseURL: nil) // $ Alert let localSafeURL = URL(string: "about:blank") let localURL = URL(string: "http://example.com/") @@ -183,9 +183,9 @@ func testWKWebView() { webview.loadHTMLString(localString, baseURL: localURL!) // GOOD: a presumed safe baseURL is specified webview.loadHTMLString(remoteString, baseURL: localURL!) // GOOD: a presumed safe baseURL is specified webview.loadHTMLString(localString, baseURL: remoteURL!) // GOOD: the HTML data is local - webview.loadHTMLString(remoteString, baseURL: remoteURL!) // BAD + webview.loadHTMLString(remoteString, baseURL: remoteURL!) // $ Alert webview.loadHTMLString(localString, baseURL: remoteURL2!) // GOOD: the HTML data is local - webview.loadHTMLString(remoteString, baseURL: remoteURL2!) // BAD + webview.loadHTMLString(remoteString, baseURL: remoteURL2!) // $ Alert let localRequest = URLRequest(url: localURL!) let remoteRequest = URLRequest(url: remoteURL!) @@ -198,7 +198,7 @@ func testWKWebView() { webview.load(localData, mimeType: "text/html", characterEncodingName: "utf-8", baseURL: localSafeURL!) // GOOD: the data is local webview.load(remoteData, mimeType: "text/html", characterEncodingName: "utf-8", baseURL: localSafeURL!) // GOOD: a safe baseURL is specified webview.load(localData, mimeType: "text/html", characterEncodingName: "utf-8", baseURL: remoteURL!) // GOOD: the HTML data is local - webview.load(remoteData, mimeType: "text/html", characterEncodingName: "utf-8", baseURL: remoteURL!) // BAD + webview.load(remoteData, mimeType: "text/html", characterEncodingName: "utf-8", baseURL: remoteURL!) // $ Alert } func testQHelpExamples() { @@ -207,7 +207,7 @@ func testQHelpExamples() { // ... - webview.loadHTMLString(htmlData, baseURL: nil) // BAD + webview.loadHTMLString(htmlData, baseURL: nil) // $ Alert webview.loadHTMLString(htmlData, baseURL: URL(string: "about:blank")) // GOOD } diff --git a/swift/ql/test/query-tests/Security/CWE-089/GRDB.swift b/swift/ql/test/query-tests/Security/CWE-089/GRDB.swift index b0319c84eb5..3bdffaa272b 100644 --- a/swift/ql/test/query-tests/Security/CWE-089/GRDB.swift +++ b/swift/ql/test/query-tests/Security/CWE-089/GRDB.swift @@ -101,54 +101,54 @@ class CommonTableExpression { func test(database: Database) throws { let localString = "user" - let remoteString = try String(contentsOf: URL(string: "http://example.com/")!) + let remoteString = try String(contentsOf: URL(string: "http://example.com/")!) // $ Source - let _ = database.allStatements(sql: remoteString) // BAD + let _ = database.allStatements(sql: remoteString) // $ Alert let _ = database.allStatements(sql: localString) // GOOD - let _ = database.allStatements(sql: remoteString, arguments: nil) // BAD + let _ = database.allStatements(sql: remoteString, arguments: nil) // $ Alert let _ = database.allStatements(sql: localString, arguments: nil) // GOOD - let _ = database.cachedStatement(sql: remoteString) // BAD + let _ = database.cachedStatement(sql: remoteString) // $ Alert let _ = database.cachedStatement(sql: localString) // GOOD - let _ = database.internalCachedStatement(sql: remoteString) // BAD + let _ = database.internalCachedStatement(sql: remoteString) // $ Alert let _ = database.internalCachedStatement(sql: localString) // GOOD - database.execute(sql: remoteString) // BAD + database.execute(sql: remoteString) // $ Alert database.execute(sql: localString) // GOOD - database.execute(sql: remoteString, arguments: StatementArguments()) // BAD + database.execute(sql: remoteString, arguments: StatementArguments()) // $ Alert database.execute(sql: localString, arguments: StatementArguments()) // GOOD - let _ = database.makeStatement(sql: remoteString) // BAD + let _ = database.makeStatement(sql: remoteString) // $ Alert let _ = database.makeStatement(sql: localString) // GOOD - let _ = database.makeStatement(sql: remoteString, prepFlags: 0) // BAD + let _ = database.makeStatement(sql: remoteString, prepFlags: 0) // $ Alert let _ = database.makeStatement(sql: localString, prepFlags: 0) // GOOD } func testSqlRequest() throws { let localString = "user" - let remoteString = try String(contentsOf: URL(string: "http://example.com/")!) + let remoteString = try String(contentsOf: URL(string: "http://example.com/")!) // $ Source - let _ = SQLRequest(stringLiteral: remoteString) // BAD + let _ = SQLRequest(stringLiteral: remoteString) // $ Alert let _ = SQLRequest(stringLiteral: localString) // GOOD - let _ = SQLRequest(unicodeScalarLiteral: remoteString) // BAD + let _ = SQLRequest(unicodeScalarLiteral: remoteString) // $ Alert let _ = SQLRequest(unicodeScalarLiteral: localString) // GOOD - let _ = SQLRequest(extendedGraphemeClusterLiteral: remoteString) // BAD + let _ = SQLRequest(extendedGraphemeClusterLiteral: remoteString) // $ Alert let _ = SQLRequest(extendedGraphemeClusterLiteral: localString) // GOOD - let _ = SQLRequest(stringInterpolation: remoteString) // BAD + let _ = SQLRequest(stringInterpolation: remoteString) // $ Alert let _ = SQLRequest(stringInterpolation: localString) // GOOD - let _ = SQLRequest(sql: remoteString) // BAD - let _ = SQLRequest(sql: remoteString, arguments: StatementArguments()) // BAD - let _ = SQLRequest(sql: remoteString, arguments: StatementArguments(), adapter: nil) // BAD - let _ = SQLRequest(sql: remoteString, arguments: StatementArguments(), cached: false) // BAD - let _ = SQLRequest(sql: remoteString, arguments: StatementArguments(), adapter: nil, cached: false) // BAD - let _ = SQLRequest(sql: remoteString, adapter: nil) // BAD - let _ = SQLRequest(sql: remoteString, adapter: nil, cached: false) // BAD - let _ = SQLRequest(sql: remoteString, cached: false) // BAD + let _ = SQLRequest(sql: remoteString) // $ Alert + let _ = SQLRequest(sql: remoteString, arguments: StatementArguments()) // $ Alert + let _ = SQLRequest(sql: remoteString, arguments: StatementArguments(), adapter: nil) // $ Alert + let _ = SQLRequest(sql: remoteString, arguments: StatementArguments(), cached: false) // $ Alert + let _ = SQLRequest(sql: remoteString, arguments: StatementArguments(), adapter: nil, cached: false) // $ Alert + let _ = SQLRequest(sql: remoteString, adapter: nil) // $ Alert + let _ = SQLRequest(sql: remoteString, adapter: nil, cached: false) // $ Alert + let _ = SQLRequest(sql: remoteString, cached: false) // $ Alert let _ = SQLRequest(sql: localString) // GOOD let _ = SQLRequest(sql: localString, arguments: StatementArguments()) // GOOD let _ = SQLRequest(sql: localString, arguments: StatementArguments(), adapter: nil) // GOOD @@ -161,15 +161,15 @@ func testSqlRequest() throws { func testSql() throws { let localString = "user" - let remoteString = try String(contentsOf: URL(string: "http://example.com/")!) + let remoteString = try String(contentsOf: URL(string: "http://example.com/")!) // $ Source - let _ = SQL(stringLiteral: remoteString) // BAD - let _ = SQL(unicodeScalarLiteral: remoteString) // BAD - let _ = SQL(extendedGraphemeClusterLiteral: remoteString) // BAD - let _ = SQL(stringInterpolation: remoteString) // BAD - let _ = SQL(sql: remoteString) // BAD + let _ = SQL(stringLiteral: remoteString) // $ Alert + let _ = SQL(unicodeScalarLiteral: remoteString) // $ Alert + let _ = SQL(extendedGraphemeClusterLiteral: remoteString) // $ Alert + let _ = SQL(stringInterpolation: remoteString) // $ Alert + let _ = SQL(sql: remoteString) // $ Alert let sql1 = SQL(stringLiteral: "") - sql1.append(sql: remoteString) // BAD + sql1.append(sql: remoteString) // $ Alert let _ = SQL(stringLiteral: localString) // GOOD let _ = SQL(unicodeScalarLiteral: localString) // GOOD @@ -182,34 +182,34 @@ func testSql() throws { func test(tableDefinition: TableDefinition) throws { let localString = "user" - let remoteString = try String(contentsOf: URL(string: "http://example.com/")!) + let remoteString = try String(contentsOf: URL(string: "http://example.com/")!) // $ Source - tableDefinition.column(sql: remoteString) // BAD + tableDefinition.column(sql: remoteString) // $ Alert tableDefinition.column(sql: localString) // GOOD - tableDefinition.check(sql: remoteString) // BAD + tableDefinition.check(sql: remoteString) // $ Alert tableDefinition.check(sql: localString) // GOOD - tableDefinition.constraint(sql: remoteString) // BAD + tableDefinition.constraint(sql: remoteString) // $ Alert tableDefinition.constraint(sql: localString) // GOOD } func test(tableAlteration: TableAlteration) throws { let localString = "user" - let remoteString = try String(contentsOf: URL(string: "http://example.com/")!) + let remoteString = try String(contentsOf: URL(string: "http://example.com/")!) // $ Source - tableAlteration.addColumn(sql: remoteString) // BAD + tableAlteration.addColumn(sql: remoteString) // $ Alert tableAlteration.addColumn(sql: localString) // GOOD } func test(columnDefinition: ColumnDefinition) throws { let localString = "user" - let remoteString = try String(contentsOf: URL(string: "http://example.com/")!) + let remoteString = try String(contentsOf: URL(string: "http://example.com/")!) // $ Source - let _ = columnDefinition.check(sql: remoteString) // BAD - let _ = columnDefinition.defaults(sql: remoteString) // BAD - let _ = columnDefinition.generatedAs(sql: remoteString) // BAD - let _ = columnDefinition.generatedAs(sql: remoteString, .virtual) // BAD + let _ = columnDefinition.check(sql: remoteString) // $ Alert + let _ = columnDefinition.defaults(sql: remoteString) // $ Alert + let _ = columnDefinition.generatedAs(sql: remoteString) // $ Alert + let _ = columnDefinition.generatedAs(sql: remoteString, .virtual) // $ Alert let _ = columnDefinition.check(sql: localString) // GOOD let _ = columnDefinition.defaults(sql: localString) // GOOD @@ -219,67 +219,67 @@ func test(columnDefinition: ColumnDefinition) throws { func testTableRecord() throws { let localString = "user" - let remoteString = try String(contentsOf: URL(string: "http://example.com/")!) + let remoteString = try String(contentsOf: URL(string: "http://example.com/")!) // $ Source - let _ = TableRecord.select(sql: remoteString) // BAD - let _ = TableRecord.select(sql: remoteString, arguments: StatementArguments()) // BAD + let _ = TableRecord.select(sql: remoteString) // $ Alert + let _ = TableRecord.select(sql: remoteString, arguments: StatementArguments()) // $ Alert let _ = TableRecord.select(sql: localString) // GOOD let _ = TableRecord.select(sql: localString, arguments: StatementArguments()) // GOOD - let _ = TableRecord.filter(sql: remoteString) // BAD - let _ = TableRecord.filter(sql: remoteString, arguments: StatementArguments()) // BAD + let _ = TableRecord.filter(sql: remoteString) // $ Alert + let _ = TableRecord.filter(sql: remoteString, arguments: StatementArguments()) // $ Alert let _ = TableRecord.filter(sql: localString) // GOOD let _ = TableRecord.filter(sql: localString, arguments: StatementArguments()) // GOOD - let _ = TableRecord.order(sql: remoteString) // BAD - let _ = TableRecord.order(sql: remoteString, arguments: StatementArguments()) // BAD + let _ = TableRecord.order(sql: remoteString) // $ Alert + let _ = TableRecord.order(sql: remoteString, arguments: StatementArguments()) // $ Alert let _ = TableRecord.order(sql: localString) // GOOD let _ = TableRecord.order(sql: localString, arguments: StatementArguments()) // GOOD } func test(statementCache: StatementCache) throws { let localString = "user" - let remoteString = try String(contentsOf: URL(string: "http://example.com/")!) + let remoteString = try String(contentsOf: URL(string: "http://example.com/")!) // $ Source - let _ = statementCache.statement(remoteString) // BAD + let _ = statementCache.statement(remoteString) // $ Alert let _ = statementCache.statement(localString) // GOOD } func test(row: Row, stmt: Statement) throws { let localString = "user" - let remoteString = try String(contentsOf: URL(string: "http://example.com/")!) + let remoteString = try String(contentsOf: URL(string: "http://example.com/")!) // $ Source - row.fetchCursor(stmt, sql: remoteString) // BAD - row.fetchCursor(stmt, sql: remoteString, arguments: StatementArguments()) // BAD - row.fetchCursor(stmt, sql: remoteString, adapter: nil) // BAD - row.fetchCursor(stmt, sql: remoteString, arguments: StatementArguments(), adapter: nil) // BAD + row.fetchCursor(stmt, sql: remoteString) // $ Alert + row.fetchCursor(stmt, sql: remoteString, arguments: StatementArguments()) // $ Alert + row.fetchCursor(stmt, sql: remoteString, adapter: nil) // $ Alert + row.fetchCursor(stmt, sql: remoteString, arguments: StatementArguments(), adapter: nil) // $ Alert row.fetchCursor(stmt, sql: localString) // GOOD row.fetchCursor(stmt, sql: localString, arguments: StatementArguments()) // GOOD row.fetchCursor(stmt, sql: localString, adapter: nil) // GOOD row.fetchCursor(stmt, sql: localString, arguments: StatementArguments(), adapter: nil) // GOOD - row.fetchAll(stmt, sql: remoteString) // BAD - row.fetchAll(stmt, sql: remoteString, arguments: StatementArguments()) // BAD - row.fetchAll(stmt, sql: remoteString, adapter: nil) // BAD - row.fetchAll(stmt, sql: remoteString, arguments: StatementArguments(), adapter: nil) // BAD + row.fetchAll(stmt, sql: remoteString) // $ Alert + row.fetchAll(stmt, sql: remoteString, arguments: StatementArguments()) // $ Alert + row.fetchAll(stmt, sql: remoteString, adapter: nil) // $ Alert + row.fetchAll(stmt, sql: remoteString, arguments: StatementArguments(), adapter: nil) // $ Alert row.fetchAll(stmt, sql: localString) // GOOD row.fetchAll(stmt, sql: localString, arguments: StatementArguments()) // GOOD row.fetchAll(stmt, sql: localString, adapter: nil) // GOOD row.fetchAll(stmt, sql: localString, arguments: StatementArguments(), adapter: nil) // GOOD - row.fetchOne(stmt, sql: remoteString) // BAD - row.fetchOne(stmt, sql: remoteString, arguments: StatementArguments()) // BAD - row.fetchOne(stmt, sql: remoteString, adapter: nil) // BAD - row.fetchOne(stmt, sql: remoteString, arguments: StatementArguments(), adapter: nil) // BAD + row.fetchOne(stmt, sql: remoteString) // $ Alert + row.fetchOne(stmt, sql: remoteString, arguments: StatementArguments()) // $ Alert + row.fetchOne(stmt, sql: remoteString, adapter: nil) // $ Alert + row.fetchOne(stmt, sql: remoteString, arguments: StatementArguments(), adapter: nil) // $ Alert row.fetchOne(stmt, sql: localString) // GOOD row.fetchOne(stmt, sql: localString, arguments: StatementArguments()) // GOOD row.fetchOne(stmt, sql: localString, adapter: nil) // GOOD row.fetchOne(stmt, sql: localString, arguments: StatementArguments(), adapter: nil) // GOOD - row.fetchSet(stmt, sql: remoteString) // BAD - row.fetchSet(stmt, sql: remoteString, arguments: StatementArguments()) // BAD - row.fetchSet(stmt, sql: remoteString, adapter: nil) // BAD - row.fetchSet(stmt, sql: remoteString, arguments: StatementArguments(), adapter: nil) // BAD + row.fetchSet(stmt, sql: remoteString) // $ Alert + row.fetchSet(stmt, sql: remoteString, arguments: StatementArguments()) // $ Alert + row.fetchSet(stmt, sql: remoteString, adapter: nil) // $ Alert + row.fetchSet(stmt, sql: remoteString, arguments: StatementArguments(), adapter: nil) // $ Alert row.fetchSet(stmt, sql: localString) // GOOD row.fetchSet(stmt, sql: localString, arguments: StatementArguments()) // GOOD row.fetchSet(stmt, sql: localString, adapter: nil) // GOOD @@ -288,39 +288,39 @@ func test(row: Row, stmt: Statement) throws { func test(databaseValueConvertible: DatabaseValueConvertible, stmt: Statement) throws { let localString = "user" - let remoteString = try String(contentsOf: URL(string: "http://example.com/")!) + let remoteString = try String(contentsOf: URL(string: "http://example.com/")!) // $ Source - databaseValueConvertible.fetchCursor(stmt, sql: remoteString) // BAD - databaseValueConvertible.fetchCursor(stmt, sql: remoteString, arguments: StatementArguments()) // BAD - databaseValueConvertible.fetchCursor(stmt, sql: remoteString, adapter: nil) // BAD - databaseValueConvertible.fetchCursor(stmt, sql: remoteString, arguments: StatementArguments(), adapter: nil) // BAD + databaseValueConvertible.fetchCursor(stmt, sql: remoteString) // $ Alert + databaseValueConvertible.fetchCursor(stmt, sql: remoteString, arguments: StatementArguments()) // $ Alert + databaseValueConvertible.fetchCursor(stmt, sql: remoteString, adapter: nil) // $ Alert + databaseValueConvertible.fetchCursor(stmt, sql: remoteString, arguments: StatementArguments(), adapter: nil) // $ Alert databaseValueConvertible.fetchCursor(stmt, sql: localString) // GOOD databaseValueConvertible.fetchCursor(stmt, sql: localString, arguments: StatementArguments()) // GOOD databaseValueConvertible.fetchCursor(stmt, sql: localString, adapter: nil) // GOOD databaseValueConvertible.fetchCursor(stmt, sql: localString, arguments: StatementArguments(), adapter: nil) // GOOD - databaseValueConvertible.fetchAll(stmt, sql: remoteString) // BAD - databaseValueConvertible.fetchAll(stmt, sql: remoteString, arguments: StatementArguments()) // BAD - databaseValueConvertible.fetchAll(stmt, sql: remoteString, adapter: nil) // BAD - databaseValueConvertible.fetchAll(stmt, sql: remoteString, arguments: StatementArguments(), adapter: nil) // BAD + databaseValueConvertible.fetchAll(stmt, sql: remoteString) // $ Alert + databaseValueConvertible.fetchAll(stmt, sql: remoteString, arguments: StatementArguments()) // $ Alert + databaseValueConvertible.fetchAll(stmt, sql: remoteString, adapter: nil) // $ Alert + databaseValueConvertible.fetchAll(stmt, sql: remoteString, arguments: StatementArguments(), adapter: nil) // $ Alert databaseValueConvertible.fetchAll(stmt, sql: localString) // GOOD databaseValueConvertible.fetchAll(stmt, sql: localString, arguments: StatementArguments()) // GOOD databaseValueConvertible.fetchAll(stmt, sql: localString, adapter: nil) // GOOD databaseValueConvertible.fetchAll(stmt, sql: localString, arguments: StatementArguments(), adapter: nil) // GOOD - databaseValueConvertible.fetchOne(stmt, sql: remoteString) // BAD - databaseValueConvertible.fetchOne(stmt, sql: remoteString, arguments: StatementArguments()) // BAD - databaseValueConvertible.fetchOne(stmt, sql: remoteString, adapter: nil) // BAD - databaseValueConvertible.fetchOne(stmt, sql: remoteString, arguments: StatementArguments(), adapter: nil) // BAD + databaseValueConvertible.fetchOne(stmt, sql: remoteString) // $ Alert + databaseValueConvertible.fetchOne(stmt, sql: remoteString, arguments: StatementArguments()) // $ Alert + databaseValueConvertible.fetchOne(stmt, sql: remoteString, adapter: nil) // $ Alert + databaseValueConvertible.fetchOne(stmt, sql: remoteString, arguments: StatementArguments(), adapter: nil) // $ Alert databaseValueConvertible.fetchOne(stmt, sql: localString) // GOOD databaseValueConvertible.fetchOne(stmt, sql: localString, arguments: StatementArguments()) // GOOD databaseValueConvertible.fetchOne(stmt, sql: localString, adapter: nil) // GOOD databaseValueConvertible.fetchOne(stmt, sql: localString, arguments: StatementArguments(), adapter: nil) // GOOD - databaseValueConvertible.fetchSet(stmt, sql: remoteString) // BAD - databaseValueConvertible.fetchSet(stmt, sql: remoteString, arguments: StatementArguments()) // BAD - databaseValueConvertible.fetchSet(stmt, sql: remoteString, adapter: nil) // BAD - databaseValueConvertible.fetchSet(stmt, sql: remoteString, arguments: StatementArguments(), adapter: nil) // BAD + databaseValueConvertible.fetchSet(stmt, sql: remoteString) // $ Alert + databaseValueConvertible.fetchSet(stmt, sql: remoteString, arguments: StatementArguments()) // $ Alert + databaseValueConvertible.fetchSet(stmt, sql: remoteString, adapter: nil) // $ Alert + databaseValueConvertible.fetchSet(stmt, sql: remoteString, arguments: StatementArguments(), adapter: nil) // $ Alert databaseValueConvertible.fetchSet(stmt, sql: localString) // GOOD databaseValueConvertible.fetchSet(stmt, sql: localString, arguments: StatementArguments()) // GOOD databaseValueConvertible.fetchSet(stmt, sql: localString, adapter: nil) // GOOD @@ -329,26 +329,26 @@ func test(databaseValueConvertible: DatabaseValueConvertible, stmt: Statement) t func testSqlStatementCursor(database: Database) throws { let localString = "user" - let remoteString = try String(contentsOf: URL(string: "http://example.com/")!) + let remoteString = try String(contentsOf: URL(string: "http://example.com/")!) // $ Source - let _ = SQLStatementCursor(database: database, sql: remoteString, arguments: StatementArguments()) // BAD - let _ = SQLStatementCursor(database: database, sql: remoteString, arguments: StatementArguments(), prepFlags: 0) // BAD + let _ = SQLStatementCursor(database: database, sql: remoteString, arguments: StatementArguments()) // $ Alert + let _ = SQLStatementCursor(database: database, sql: remoteString, arguments: StatementArguments(), prepFlags: 0) // $ Alert let _ = SQLStatementCursor(database: database, sql: localString, arguments: StatementArguments()) // GOOD let _ = SQLStatementCursor(database: database, sql: localString, arguments: StatementArguments(), prepFlags: 0) // GOOD } func testCommonTableExpression() throws { let localString = "user" - let remoteString = try String(contentsOf: URL(string: "http://example.com/")!) + let remoteString = try String(contentsOf: URL(string: "http://example.com/")!) // $ Source - let _ = CommonTableExpression(named: "", sql: remoteString) // BAD - let _ = CommonTableExpression(named: "", sql: remoteString, arguments: StatementArguments()) // BAD - let _ = CommonTableExpression(named: "", columns: [""], sql: remoteString) // BAD - let _ = CommonTableExpression(named: "", columns: [""], sql: remoteString, arguments: StatementArguments()) // BAD - let _ = CommonTableExpression(recursive: false, named: "", sql: remoteString) // BAD - let _ = CommonTableExpression(recursive: false, named: "", columns: [""], sql: remoteString) // BAD - let _ = CommonTableExpression(recursive: false, named: "", sql: remoteString, arguments: StatementArguments()) // BAD - let _ = CommonTableExpression(recursive: false, named: "", columns: [""], sql: remoteString, arguments: StatementArguments()) // BAD + let _ = CommonTableExpression(named: "", sql: remoteString) // $ Alert + let _ = CommonTableExpression(named: "", sql: remoteString, arguments: StatementArguments()) // $ Alert + let _ = CommonTableExpression(named: "", columns: [""], sql: remoteString) // $ Alert + let _ = CommonTableExpression(named: "", columns: [""], sql: remoteString, arguments: StatementArguments()) // $ Alert + let _ = CommonTableExpression(recursive: false, named: "", sql: remoteString) // $ Alert + let _ = CommonTableExpression(recursive: false, named: "", columns: [""], sql: remoteString) // $ Alert + let _ = CommonTableExpression(recursive: false, named: "", sql: remoteString, arguments: StatementArguments()) // $ Alert + let _ = CommonTableExpression(recursive: false, named: "", columns: [""], sql: remoteString, arguments: StatementArguments()) // $ Alert let _ = CommonTableExpression(named: "", sql: localString) // GOOD let _ = CommonTableExpression(named: "", sql: localString, arguments: StatementArguments()) // GOOD let _ = CommonTableExpression(named: "", columns: [""], sql: localString) // GOOD diff --git a/swift/ql/test/query-tests/Security/CWE-089/SQLite.swift b/swift/ql/test/query-tests/Security/CWE-089/SQLite.swift index f9a6b41340c..5973866fb25 100644 --- a/swift/ql/test/query-tests/Security/CWE-089/SQLite.swift +++ b/swift/ql/test/query-tests/Security/CWE-089/SQLite.swift @@ -59,7 +59,7 @@ class Connection { func test_sqlite_swift_api(db: Connection) throws { let localString = "user" - let remoteString = try String(contentsOf: URL(string: "http://example.com/")!) + let remoteString = try String(contentsOf: URL(string: "http://example.com/")!) // $ Source let remoteNumber = Int(remoteString) ?? 0 let unsafeQuery1 = remoteString @@ -70,9 +70,9 @@ func test_sqlite_swift_api(db: Connection) throws { // --- execute --- - try db.execute(unsafeQuery1) // BAD - try db.execute(unsafeQuery2) // BAD - try db.execute(unsafeQuery3) // BAD + try db.execute(unsafeQuery1) // $ Alert + try db.execute(unsafeQuery2) // $ Alert + try db.execute(unsafeQuery3) // $ Alert try db.execute(safeQuery1) // GOOD try db.execute(safeQuery2) // GOOD @@ -80,7 +80,7 @@ func test_sqlite_swift_api(db: Connection) throws { let varQuery = "SELECT * FROM users WHERE username=?" - let stmt1 = try db.prepare(unsafeQuery3) // BAD + let stmt1 = try db.prepare(unsafeQuery3) // $ Alert try stmt1.run() let stmt2 = try db.prepare(varQuery, localString) // GOOD @@ -92,31 +92,31 @@ func test_sqlite_swift_api(db: Connection) throws { let stmt4 = try Statement(db, localString) // GOOD try stmt4.run() - let stmt5 = try Statement(db, remoteString) // BAD + let stmt5 = try Statement(db, remoteString) // $ Alert try stmt5.run() // --- more variants --- - let stmt6 = try db.prepare(unsafeQuery1, "") // BAD + let stmt6 = try db.prepare(unsafeQuery1, "") // $ Alert try stmt6.run() - let stmt7 = try db.prepare(unsafeQuery1, [""]) // BAD + let stmt7 = try db.prepare(unsafeQuery1, [""]) // $ Alert try stmt7.run() - let stmt8 = try db.prepare(unsafeQuery1, ["username": ""]) // BAD + let stmt8 = try db.prepare(unsafeQuery1, ["username": ""]) // $ Alert try stmt8.run() - try db.run(unsafeQuery1, "") // BAD + try db.run(unsafeQuery1, "") // $ Alert - try db.run(unsafeQuery1, [""]) // BAD + try db.run(unsafeQuery1, [""]) // $ Alert - try db.run(unsafeQuery1, ["username": ""]) // BAD + try db.run(unsafeQuery1, ["username": ""]) // $ Alert - try db.scalar(unsafeQuery1, "") // BAD + try db.scalar(unsafeQuery1, "") // $ Alert - try db.scalar(unsafeQuery1, [""]) // BAD + try db.scalar(unsafeQuery1, [""]) // $ Alert - try db.scalar(unsafeQuery1, ["username": ""]) // BAD + try db.scalar(unsafeQuery1, ["username": ""]) // $ Alert let stmt9 = try db.prepare(varQuery) // GOOD try stmt9.bind(remoteString) // GOOD @@ -129,5 +129,5 @@ func test_sqlite_swift_api(db: Connection) throws { try stmt9.scalar([remoteString]) // GOOD try stmt9.scalar(["username": remoteString]) // GOOD - try Statement(db, remoteString).run() // BAD + try Statement(db, remoteString).run() // $ Alert } diff --git a/swift/ql/test/query-tests/Security/CWE-089/SqlInjection.qlref b/swift/ql/test/query-tests/Security/CWE-089/SqlInjection.qlref index eaf19a94546..654631d8a09 100644 --- a/swift/ql/test/query-tests/Security/CWE-089/SqlInjection.qlref +++ b/swift/ql/test/query-tests/Security/CWE-089/SqlInjection.qlref @@ -1 +1,2 @@ -queries/Security/CWE-089/SqlInjection.ql \ No newline at end of file +query: queries/Security/CWE-089/SqlInjection.ql +postprocess: utils/test/InlineExpectationsTestQuery.ql diff --git a/swift/ql/test/query-tests/Security/CWE-089/other.swift b/swift/ql/test/query-tests/Security/CWE-089/other.swift index 52cafbb1545..0974d03937e 100644 --- a/swift/ql/test/query-tests/Security/CWE-089/other.swift +++ b/swift/ql/test/query-tests/Security/CWE-089/other.swift @@ -43,21 +43,21 @@ class MyDatabase { // --- tests --- func test_heuristic(db: MyDatabase) throws { - let remoteString = try String(contentsOf: URL(string: "http://example.com/")!) + let remoteString = try String(contentsOf: URL(string: "http://example.com/")!) // $ Source _ = MyDatabase() // GOOD _ = MyDatabase(sql: "some_fixed_sql") // GOOD - _ = MyDatabase(sql: remoteString) // BAD + _ = MyDatabase(sql: remoteString) // $ Alert - db.execute1(remoteString) // BAD - db.execute2(remoteString) // BAD - db.execute3(NSString(string: remoteString)) // BAD - db.execute4(remoteString as! Sql) // BAD + db.execute1(remoteString) // $ Alert + db.execute2(remoteString) // $ Alert + db.execute3(NSString(string: remoteString)) // $ Alert + db.execute4(remoteString as! Sql) // $ Alert - db.query(sql: remoteString) // BAD - db.query(sqlLiteral: remoteString) // BAD [NOT DETECTED] - db.query(sqlStatement: remoteString) // BAD [NOT DETECTED] - db.query(sqliteStatement: remoteString) // BAD [NOT DETECTED] + db.query(sql: remoteString) // $ Alert + db.query(sqlLiteral: remoteString) // $ MISSING: Alert // BAD [NOT DETECTED] + db.query(sqlStatement: remoteString) // $ MISSING: Alert // BAD [NOT DETECTED] + db.query(sqliteStatement: remoteString) // $ MISSING: Alert // BAD [NOT DETECTED] db.doSomething(sqlIndex: Int(remoteString) ?? 0) // GOOD db.doSomething(sqliteContext: remoteString as! Sql) // GOOD diff --git a/swift/ql/test/query-tests/Security/CWE-089/sqlite3_c_api.swift b/swift/ql/test/query-tests/Security/CWE-089/sqlite3_c_api.swift index 8498d89d68d..b4e7451b916 100644 --- a/swift/ql/test/query-tests/Security/CWE-089/sqlite3_c_api.swift +++ b/swift/ql/test/query-tests/Security/CWE-089/sqlite3_c_api.swift @@ -119,7 +119,7 @@ func sqlite3_finalize( func test_sqlite3_c_api(db: OpaquePointer?, buffer: UnsafeMutablePointer) { let localString = "user" - let remoteString = try! String(contentsOf: URL(string: "http://example.com/")!) + let remoteString = try! String(contentsOf: URL(string: "http://example.com/")!) // $ Source let remoteNumber = Int(remoteString) ?? 0 let unsafeQuery1 = remoteString @@ -130,9 +130,9 @@ func test_sqlite3_c_api(db: OpaquePointer?, buffer: UnsafeMutablePointer) // --- exec --- - let result1 = sqlite3_exec(db, unsafeQuery1, nil, nil, nil) // BAD - let result2 = sqlite3_exec(db, unsafeQuery2, nil, nil, nil) // BAD - let result3 = sqlite3_exec(db, unsafeQuery3, nil, nil, nil) // BAD + let result1 = sqlite3_exec(db, unsafeQuery1, nil, nil, nil) // $ Alert + let result2 = sqlite3_exec(db, unsafeQuery2, nil, nil, nil) // $ Alert + let result3 = sqlite3_exec(db, unsafeQuery3, nil, nil, nil) // $ Alert let result4 = sqlite3_exec(db, safeQuery1, nil, nil, nil) // GOOD let result5 = sqlite3_exec(db, safeQuery2, nil, nil, nil) // GOOD @@ -142,7 +142,7 @@ func test_sqlite3_c_api(db: OpaquePointer?, buffer: UnsafeMutablePointer) var stmt1: OpaquePointer? - if (sqlite3_prepare(db, unsafeQuery3, -1, &stmt1, nil) == SQLITE_OK) { // BAD + if (sqlite3_prepare(db, unsafeQuery3, -1, &stmt1, nil) == SQLITE_OK) { // $ Alert let result = sqlite3_step(stmt1) // ... } @@ -172,7 +172,7 @@ func test_sqlite3_c_api(db: OpaquePointer?, buffer: UnsafeMutablePointer) var stmt4: OpaquePointer? - if (sqlite3_prepare_v2(db, unsafeQuery3, -1, &stmt4, nil) == SQLITE_OK) { // BAD + if (sqlite3_prepare_v2(db, unsafeQuery3, -1, &stmt4, nil) == SQLITE_OK) { // $ Alert let result = sqlite3_step(stmt4) // ... } @@ -180,7 +180,7 @@ func test_sqlite3_c_api(db: OpaquePointer?, buffer: UnsafeMutablePointer) var stmt5: OpaquePointer? - if (sqlite3_prepare_v3(db, unsafeQuery3, -1, 0, &stmt5, nil) == SQLITE_OK) { // BAD + if (sqlite3_prepare_v3(db, unsafeQuery3, -1, 0, &stmt5, nil) == SQLITE_OK) { // $ Alert let result = sqlite3_step(stmt5) // ... } @@ -191,7 +191,7 @@ func test_sqlite3_c_api(db: OpaquePointer?, buffer: UnsafeMutablePointer) var stmt6: OpaquePointer? - if (sqlite3_prepare16(db, buffer, Int32(data.count), &stmt6, nil) == SQLITE_OK) { // BAD + if (sqlite3_prepare16(db, buffer, Int32(data.count), &stmt6, nil) == SQLITE_OK) { // $ Alert let result = sqlite3_step(stmt6) // ... } @@ -199,7 +199,7 @@ func test_sqlite3_c_api(db: OpaquePointer?, buffer: UnsafeMutablePointer) var stmt7: OpaquePointer? - if (sqlite3_prepare16_v2(db, buffer, Int32(data.count), &stmt7, nil) == SQLITE_OK) { // BAD + if (sqlite3_prepare16_v2(db, buffer, Int32(data.count), &stmt7, nil) == SQLITE_OK) { // $ Alert let result = sqlite3_step(stmt7) // ... } @@ -207,7 +207,7 @@ func test_sqlite3_c_api(db: OpaquePointer?, buffer: UnsafeMutablePointer) var stmt8: OpaquePointer? - if (sqlite3_prepare16_v3(db, buffer, Int32(data.count), 0, &stmt8, nil) == SQLITE_OK) { // BAD + if (sqlite3_prepare16_v3(db, buffer, Int32(data.count), 0, &stmt8, nil) == SQLITE_OK) { // $ Alert let result = sqlite3_step(stmt8) // ... } diff --git a/swift/ql/test/query-tests/Security/CWE-116/BadTagFilter.qlref b/swift/ql/test/query-tests/Security/CWE-116/BadTagFilter.qlref index 8186dfa236f..67e973ba99e 100644 --- a/swift/ql/test/query-tests/Security/CWE-116/BadTagFilter.qlref +++ b/swift/ql/test/query-tests/Security/CWE-116/BadTagFilter.qlref @@ -1 +1,2 @@ -queries/Security/CWE-116/BadTagFilter.ql \ No newline at end of file +query: queries/Security/CWE-116/BadTagFilter.ql +postprocess: utils/test/InlineExpectationsTestQuery.ql diff --git a/swift/ql/test/query-tests/Security/CWE-116/test.swift b/swift/ql/test/query-tests/Security/CWE-116/test.swift index e2e88135dd6..be6cbc0dcdd 100644 --- a/swift/ql/test/query-tests/Security/CWE-116/test.swift +++ b/swift/ql/test/query-tests/Security/CWE-116/test.swift @@ -76,18 +76,18 @@ func myRegexpVariantsTests(myUrl: URL) throws { let tainted = String(contentsOf: myUrl) // tainted // BAD - doesn't match newlines or `` - let re1 = try Regex(#".*?<\/script>"#).ignoresCase(true) + let re1 = try Regex(#".*?<\/script>"#).ignoresCase(true) // $ Alert _ = try re1.firstMatch(in: tainted) // BAD - doesn't match `` - let re2a = try Regex(#"(?is).*?<\/script>"#) + let re2a = try Regex(#"(?is).*?<\/script>"#) // $ Alert _ = try re2a.firstMatch(in: tainted) // BAD - doesn't match `` - let re2b = try Regex(#".*?<\/script>"#).ignoresCase(true).dotMatchesNewlines(true) + let re2b = try Regex(#".*?<\/script>"#).ignoresCase(true).dotMatchesNewlines(true) // $ Alert _ = try re2b.firstMatch(in: tainted) // BAD - doesn't match `` let options2c: NSRegularExpression.Options = [.caseInsensitive, .dotMatchesLineSeparators] - let ns2c = try NSRegularExpression(pattern: #".*?<\/script>"#, options: options2c) + let ns2c = try NSRegularExpression(pattern: #".*?<\/script>"#, options: options2c) // $ Alert _ = ns2c.firstMatch(in: tainted, range: NSMakeRange(0, tainted.utf16.count)) // GOOD @@ -110,71 +110,71 @@ func myRegexpVariantsTests(myUrl: URL) throws { _ = try re5.firstMatch(in: tainted) // BAD, does not match newlines - let re6 = try Regex(#")|([^\/\s>]+)[\S\s]*?>"#) + let re16 = try Regex(#"<(?:!--([\S|\s]*?)-->)|([^\/\s>]+)[\S\s]*?>"#) // $ Alert _ = try re16.firstMatch(in: tainted) // BAD - doesn't match comments with the right capture groups - let ns16 = try NSRegularExpression(pattern: #"<(?:!--([\S|\s]*?)-->)|([^\/\s>]+)[\S\s]*?>"#) + let ns16 = try NSRegularExpression(pattern: #"<(?:!--([\S|\s]*?)-->)|([^\/\s>]+)[\S\s]*?>"#) // $ Alert _ = ns16.firstMatch(in: tainted, range: NSMakeRange(0, tainted.utf16.count)) // BAD - capture groups - let re17 = try Regex(#"<(?:(?:\/([^>]+)>)|(?:!--([\S|\s]*?)-->)|(?:([^\/\s>]+)((?:\s+[\w\-:.]+(?:\s*=\s*?(?:(?:"[^"]*")|(?:'[^']*')|[^\s"'\/>]+))?)*)[\S\s]*?(\/?)>))"#) + let re17 = try Regex(#"<(?:(?:\/([^>]+)>)|(?:!--([\S|\s]*?)-->)|(?:([^\/\s>]+)((?:\s+[\w\-:.]+(?:\s*=\s*?(?:(?:"[^"]*")|(?:'[^']*')|[^\s"'\/>]+))?)*)[\S\s]*?(\/?)>))"#) // $ Alert _ = try re17.firstMatch(in: tainted) // BAD - capture groups - let ns17 = try NSRegularExpression(pattern: #"<(?:(?:\/([^>]+)>)|(?:!--([\S|\s]*?)-->)|(?:([^\/\s>]+)((?:\s+[\w\-:.]+(?:\s*=\s*?(?:(?:"[^"]*")|(?:'[^']*')|[^\s"'\/>]+))?)*)[\S\s]*?(\/?)>))"#, options: .caseInsensitive) + let ns17 = try NSRegularExpression(pattern: #"<(?:(?:\/([^>]+)>)|(?:!--([\S|\s]*?)-->)|(?:([^\/\s>]+)((?:\s+[\w\-:.]+(?:\s*=\s*?(?:(?:"[^"]*")|(?:'[^']*')|[^\s"'\/>]+))?)*)[\S\s]*?(\/?)>))"#, options: .caseInsensitive) // $ Alert _ = ns17.firstMatch(in: tainted, range: NSMakeRange(0, tainted.utf16.count)) // BAD - too strict matching on the end tag - let ns2_1 = try NSRegularExpression(pattern: #"]*>([\s\S]*?)<\/script>"#, options: .caseInsensitive) + let ns2_1 = try NSRegularExpression(pattern: #"]*>([\s\S]*?)<\/script>"#, options: .caseInsensitive) // $ Alert _ = ns2_1.matches(in: tainted, range: NSMakeRange(0, tainted.utf16.count)) // BAD - capture groups - let ns2_2 = try NSRegularExpression(pattern: #"(<[a-z\/!$]("[^"]*"|'[^']*'|[^'">])*>|)"#, options: .caseInsensitive) + let ns2_2 = try NSRegularExpression(pattern: #"(<[a-z\/!$]("[^"]*"|'[^']*'|[^'">])*>|)"#, options: .caseInsensitive) // $ Alert _ = ns2_2.matches(in: tainted, range: NSMakeRange(0, tainted.utf16.count)) // BAD - capture groups - let ns2_3 = try NSRegularExpression(pattern: #"<(?:(?:!--([\w\W]*?)-->)|(?:!\[CDATA\[([\w\W]*?)\]\]>)|(?:!DOCTYPE([\w\W]*?)>)|(?:\?([^\s\/<>]+) ?([\w\W]*?)[?/]>)|(?:\/([A-Za-z][A-Za-z0-9\-_\:\.]*)>)|(?:([A-Za-z][A-Za-z0-9\-_\:\.]*)((?:\s+[^"'>]+(?:(?:"[^"]*")|(?:'[^']*')|[^>]*))*|\/|\s+)>))"#) + let ns2_3 = try NSRegularExpression(pattern: #"<(?:(?:!--([\w\W]*?)-->)|(?:!\[CDATA\[([\w\W]*?)\]\]>)|(?:!DOCTYPE([\w\W]*?)>)|(?:\?([^\s\/<>]+) ?([\w\W]*?)[?/]>)|(?:\/([A-Za-z][A-Za-z0-9\-_\:\.]*)>)|(?:([A-Za-z][A-Za-z0-9\-_\:\.]*)((?:\s+[^"'>]+(?:(?:"[^"]*")|(?:'[^']*')|[^>]*))*|\/|\s+)>))"#) // $ Alert _ = ns2_3.matches(in: tainted, range: NSMakeRange(0, tainted.utf16.count)) // BAD - capture groups - let ns2_4 = try NSRegularExpression(pattern: #"|<([^>]*?)>"#) + let ns2_4 = try NSRegularExpression(pattern: #"|<([^>]*?)>"#) // $ Alert _ = ns2_4.matches(in: tainted, range: NSMakeRange(0, tainted.utf16.count)) // GOOD - it's used with the ignorecase flag @@ -222,7 +222,7 @@ func myRegexpVariantsTests(myUrl: URL) throws { _ = ns2_5.matches(in: tainted, range: NSMakeRange(0, tainted.utf16.count)) // BAD - doesn't match --!> - let ns2_6 = try NSRegularExpression(pattern: #"-->"#) + let ns2_6 = try NSRegularExpression(pattern: #"-->"#) // $ Alert _ = ns2_6.matches(in: tainted, range: NSMakeRange(0, tainted.utf16.count)) // GOOD diff --git a/swift/ql/test/query-tests/Security/CWE-1204/StaticInitializationVector.qlref b/swift/ql/test/query-tests/Security/CWE-1204/StaticInitializationVector.qlref index 36f922580f7..6106d4b12ad 100644 --- a/swift/ql/test/query-tests/Security/CWE-1204/StaticInitializationVector.qlref +++ b/swift/ql/test/query-tests/Security/CWE-1204/StaticInitializationVector.qlref @@ -1 +1,2 @@ -queries/Security/CWE-1204/StaticInitializationVector.ql +query: queries/Security/CWE-1204/StaticInitializationVector.ql +postprocess: utils/test/InlineExpectationsTestQuery.ql diff --git a/swift/ql/test/query-tests/Security/CWE-1204/rncryptor.swift b/swift/ql/test/query-tests/Security/CWE-1204/rncryptor.swift index 253804cabf1..319c4c927ed 100644 --- a/swift/ql/test/query-tests/Security/CWE-1204/rncryptor.swift +++ b/swift/ql/test/query-tests/Security/CWE-1204/rncryptor.swift @@ -57,28 +57,28 @@ func test(myPassword: String) { let myKeyDerivationSettings = RNCryptorKeyDerivationSettings() let myHandler = {} let myRandomIV = Data(getRandomArray()) - let myConstIV1 = Data(0) - let myConstIV2 = Data(123) - let myConstIV3 = Data([1,2,3,4,5]) - let myConstIV4 = Data("iv") + let myConstIV1 = Data(0) // $ Source + let myConstIV2 = Data(123) // $ Source + let myConstIV3 = Data([1,2,3,4,5]) // $ Source + let myConstIV4 = Data("iv") // $ Source let mySalt = Data(0) let mySalt2 = Data(0) let _ = RNEncryptor(settings: kRNCryptorAES256Settings, encryptionKey: myKey, hmacKey: myHMACKey, iv: myRandomIV, handler: myHandler) // GOOD - let _ = RNEncryptor(settings: kRNCryptorAES256Settings, encryptionKey: myKey, hmacKey: myHMACKey, iv: myConstIV1, handler: myHandler) // BAD + let _ = RNEncryptor(settings: kRNCryptorAES256Settings, encryptionKey: myKey, hmacKey: myHMACKey, iv: myConstIV1, handler: myHandler) // $ Alert let _ = RNEncryptor(settings: kRNCryptorAES256Settings, encryptionKey: myKey, HMACKey: myHMACKey, IV: myRandomIV, handler: myHandler) // GOOD - let _ = RNEncryptor(settings: kRNCryptorAES256Settings, encryptionKey: myKey, HMACKey: myHMACKey, IV: myConstIV2, handler: myHandler) // BAD + let _ = RNEncryptor(settings: kRNCryptorAES256Settings, encryptionKey: myKey, HMACKey: myHMACKey, IV: myConstIV2, handler: myHandler) // $ Alert let _ = RNEncryptor(settings: kRNCryptorAES256Settings, password: myPassword, iv: myRandomIV, encryptionSalt: mySalt, hmacSalt: mySalt2, handler: myHandler) // GOOD - let _ = RNEncryptor(settings: kRNCryptorAES256Settings, password: myPassword, iv: myConstIV3, encryptionSalt: mySalt, hmacSalt: mySalt2, handler: myHandler) // BAD + let _ = RNEncryptor(settings: kRNCryptorAES256Settings, password: myPassword, iv: myConstIV3, encryptionSalt: mySalt, hmacSalt: mySalt2, handler: myHandler) // $ Alert let _ = RNEncryptor(settings: kRNCryptorAES256Settings, password: myPassword, IV: myRandomIV, encryptionSalt: mySalt, HMACSalt: mySalt2, handler: myHandler) // GOOD - let _ = RNEncryptor(settings: kRNCryptorAES256Settings, password: myPassword, IV: myConstIV4, encryptionSalt: mySalt, HMACSalt: mySalt2, handler: myHandler) // BAD + let _ = RNEncryptor(settings: kRNCryptorAES256Settings, password: myPassword, IV: myConstIV4, encryptionSalt: mySalt, HMACSalt: mySalt2, handler: myHandler) // $ Alert let _ = try? myEncryptor.encryptData(myData, with: kRNCryptorAES256Settings, encryptionKey: myKey, hmacKey: myHMACKey, iv: myRandomIV) // GOOD - let _ = try? myEncryptor.encryptData(myData, with: kRNCryptorAES256Settings, encryptionKey: myKey, hmacKey: myHMACKey, iv: myConstIV1) // BAD + let _ = try? myEncryptor.encryptData(myData, with: kRNCryptorAES256Settings, encryptionKey: myKey, hmacKey: myHMACKey, iv: myConstIV1) // $ Alert let _ = try? myEncryptor.encryptData(myData, withSettings: kRNCryptorAES256Settings, encryptionKey: myKey, HMACKey: myHMACKey, IV: myRandomIV) // GOOD - let _ = try? myEncryptor.encryptData(myData, withSettings: kRNCryptorAES256Settings, encryptionKey: myKey, HMACKey: myHMACKey, IV: myConstIV2) // BAD + let _ = try? myEncryptor.encryptData(myData, withSettings: kRNCryptorAES256Settings, encryptionKey: myKey, HMACKey: myHMACKey, IV: myConstIV2) // $ Alert let _ = try? myEncryptor.encryptData(myData, with: kRNCryptorAES256Settings, password: myPassword, iv: myRandomIV, encryptionSalt: mySalt, hmacSalt: mySalt2) // GOOD - let _ = try? myEncryptor.encryptData(myData, with: kRNCryptorAES256Settings, password: myPassword, iv: myConstIV3, encryptionSalt: mySalt, hmacSalt: mySalt2) // BAD + let _ = try? myEncryptor.encryptData(myData, with: kRNCryptorAES256Settings, password: myPassword, iv: myConstIV3, encryptionSalt: mySalt, hmacSalt: mySalt2) // $ Alert let _ = try? myEncryptor.encryptData(myData, withSettings: kRNCryptorAES256Settings, password: myPassword, IV: myRandomIV, encryptionSalt: mySalt, HMACSalt: mySalt2) // GOOD - let _ = try? myEncryptor.encryptData(myData, withSettings: kRNCryptorAES256Settings, password: myPassword, IV: myConstIV4, encryptionSalt: mySalt, HMACSalt: mySalt2) // BAD + let _ = try? myEncryptor.encryptData(myData, withSettings: kRNCryptorAES256Settings, password: myPassword, IV: myConstIV4, encryptionSalt: mySalt, HMACSalt: mySalt2) // $ Alert } diff --git a/swift/ql/test/query-tests/Security/CWE-1204/test.swift b/swift/ql/test/query-tests/Security/CWE-1204/test.swift index 273556ce5bb..8536996ca3a 100644 --- a/swift/ql/test/query-tests/Security/CWE-1204/test.swift +++ b/swift/ql/test/query-tests/Security/CWE-1204/test.swift @@ -51,7 +51,7 @@ final class GCM: BlockMode { enum Mode { case combined, detached } init(iv: Array, additionalAuthenticatedData: Array? = nil, tagLength: Int = 16, mode: Mode = .detached) { } convenience init(iv: Array, authenticationTag: Array, additionalAuthenticatedData: Array? = nil, mode: Mode = .detached) { - self.init(iv: iv, additionalAuthenticatedData: additionalAuthenticatedData, tagLength: authenticationTag.count, mode: mode) + self.init(iv: iv, additionalAuthenticatedData: additionalAuthenticatedData, tagLength: authenticationTag.count, mode: mode) // $ Alert } } @@ -82,7 +82,7 @@ enum Padding: PaddingProtocol { // Helper functions func getConstantString() -> String { - "this string is constant" + "this string is constant" // $ Source } func getConstantArray() -> Array { @@ -96,7 +96,7 @@ func getRandomArray() -> Array { // --- tests --- func test() { - let iv: Array = [0x2a, 0x3a, 0x80, 0x05, 0xaf, 0x46, 0x58, 0x2d, 0x66, 0x52, 0x10, 0xae, 0x86, 0xd3, 0x8e, 0x8f] + let iv: Array = [0x2a, 0x3a, 0x80, 0x05, 0xaf, 0x46, 0x58, 0x2d, 0x66, 0x52, 0x10, 0xae, 0x86, 0xd3, 0x8e, 0x8f] // $ Source let iv2 = getConstantArray() let ivString = getConstantString() @@ -109,63 +109,63 @@ func test() { let keyString = String(cString: key) // AES test cases - let ab1 = AES(key: keyString, iv: ivString) // BAD - let ab2 = AES(key: keyString, iv: ivString, padding: padding) // BAD + let ab1 = AES(key: keyString, iv: ivString) // $ Alert + let ab2 = AES(key: keyString, iv: ivString, padding: padding) // $ Alert let ag1 = AES(key: keyString, iv: randomIvString) // GOOD let ag2 = AES(key: keyString, iv: randomIvString, padding: padding) // GOOD // ChaCha20 test cases - let cb1 = ChaCha20(key: keyString, iv: ivString) // BAD + let cb1 = ChaCha20(key: keyString, iv: ivString) // $ Alert let cg1 = ChaCha20(key: keyString, iv: randomIvString) // GOOD // Blowfish test cases - let bb1 = Blowfish(key: keyString, iv: ivString) // BAD - let bb2 = Blowfish(key: keyString, iv: ivString, padding: padding) // BAD + let bb1 = Blowfish(key: keyString, iv: ivString) // $ Alert + let bb2 = Blowfish(key: keyString, iv: ivString, padding: padding) // $ Alert let bg1 = Blowfish(key: keyString, iv: randomIvString) // GOOD let bg2 = Blowfish(key: keyString, iv: randomIvString, padding: padding) // GOOD // Rabbit - let rb1 = Rabbit(key: key, iv: iv) // BAD - let rb2 = Rabbit(key: key, iv: iv2) // BAD - let rb3 = Rabbit(key: keyString, iv: ivString) // BAD + let rb1 = Rabbit(key: key, iv: iv) // $ Alert + let rb2 = Rabbit(key: key, iv: iv2) // $ Alert + let rb3 = Rabbit(key: keyString, iv: ivString) // $ Alert let rg1 = Rabbit(key: key, iv: randomIv) // GOOD let rg2 = Rabbit(key: keyString, iv: randomIvString) // GOOD // CBC - let cbcb1 = CBC(iv: iv) // BAD + let cbcb1 = CBC(iv: iv) // $ Alert let cbcg1 = CBC(iv: randomIv) // GOOD // CFB - let cfbb1 = CFB(iv: iv) // BAD - let cfbb2 = CFB(iv: iv, segmentSize: CFB.SegmentSize.cfb8) // BAD + let cfbb1 = CFB(iv: iv) // $ Alert + let cfbb2 = CFB(iv: iv, segmentSize: CFB.SegmentSize.cfb8) // $ Alert let cfbg1 = CFB(iv: randomIv) // GOOD let cfbg2 = CFB(iv: randomIv, segmentSize: CFB.SegmentSize.cfb8) // GOOD // GCM - let cgmb1 = GCM(iv: iv) // BAD - let cgmb2 = GCM(iv: iv, additionalAuthenticatedData: randomArray, tagLength: 8, mode: GCM.Mode.combined) // BAD - let cgmb3 = GCM(iv: iv, authenticationTag: randomArray, additionalAuthenticatedData: randomArray, mode: GCM.Mode.combined) // BAD + let cgmb1 = GCM(iv: iv) // $ Alert + let cgmb2 = GCM(iv: iv, additionalAuthenticatedData: randomArray, tagLength: 8, mode: GCM.Mode.combined) // $ Alert + let cgmb3 = GCM(iv: iv, authenticationTag: randomArray, additionalAuthenticatedData: randomArray, mode: GCM.Mode.combined) // $ Alert let cgmg1 = GCM(iv: randomIv) // GOOD let cgmg2 = GCM(iv: randomIv, additionalAuthenticatedData: randomArray, tagLength: 8, mode: GCM.Mode.combined) // GOOD let cgmg3 = GCM(iv: randomIv, authenticationTag: randomArray, additionalAuthenticatedData: randomArray, mode: GCM.Mode.combined) // GOOD // OFB - let ofbb1 = OFB(iv: iv) // BAD + let ofbb1 = OFB(iv: iv) // $ Alert let ofbg1 = OFB(iv: randomIv) // GOOD // PCBC - let pcbcb1 = PCBC(iv: iv) // BAD + let pcbcb1 = PCBC(iv: iv) // $ Alert let pcbcg1 = PCBC(iv: randomIv) // GOOD // CCM - let ccmb1 = CCM(iv: iv, tagLength: 0, messageLength: 0, additionalAuthenticatedData: randomArray) // BAD - let ccmb2 = CCM(iv: iv, tagLength: 0, messageLength: 0, authenticationTag: randomArray, additionalAuthenticatedData: randomArray) // BAD + let ccmb1 = CCM(iv: iv, tagLength: 0, messageLength: 0, additionalAuthenticatedData: randomArray) // $ Alert + let ccmb2 = CCM(iv: iv, tagLength: 0, messageLength: 0, authenticationTag: randomArray, additionalAuthenticatedData: randomArray) // $ Alert let ccmg1 = CCM(iv: randomIv, tagLength: 0, messageLength: 0, additionalAuthenticatedData: randomArray) // GOOD let ccmg2 = CCM(iv: randomIv, tagLength: 0, messageLength: 0, authenticationTag: randomArray, additionalAuthenticatedData: randomArray) // GOOD // CTR - let ctrb1 = CTR(iv: iv) // BAD - let ctrb2 = CTR(iv: iv, counter: 0) // BAD + let ctrb1 = CTR(iv: iv) // $ Alert + let ctrb2 = CTR(iv: iv, counter: 0) // $ Alert let ctrg1 = CTR(iv: randomIv) // GOOD let ctrg2 = CTR(iv: randomIv, counter: 0) // GOOD } diff --git a/swift/ql/test/query-tests/Security/CWE-1333/ReDoS.qlref b/swift/ql/test/query-tests/Security/CWE-1333/ReDoS.qlref index a0bdcd8a864..5294bedca63 100644 --- a/swift/ql/test/query-tests/Security/CWE-1333/ReDoS.qlref +++ b/swift/ql/test/query-tests/Security/CWE-1333/ReDoS.qlref @@ -1 +1,2 @@ -queries/Security/CWE-1333/ReDoS.ql \ No newline at end of file +query: queries/Security/CWE-1333/ReDoS.ql +postprocess: utils/test/InlineExpectationsTestQuery.ql diff --git a/swift/ql/test/query-tests/Security/CWE-1333/ReDoS.swift b/swift/ql/test/query-tests/Security/CWE-1333/ReDoS.swift index 0349bac0669..c7489a6e067 100644 --- a/swift/ql/test/query-tests/Security/CWE-1333/ReDoS.swift +++ b/swift/ql/test/query-tests/Security/CWE-1333/ReDoS.swift @@ -61,25 +61,25 @@ func myRegexpTests(myUrl: URL) throws { // Regex _ = "((a*)*b)" // GOOD (never used) - _ = try Regex("((a*)*b)") // DUBIOUS (never used) [FLAGGED] - _ = try Regex("((a*)*b)").firstMatch(in: untainted) // DUBIOUS (never used on tainted input) [FLAGGED] - _ = try Regex("((a*)*b)").firstMatch(in: tainted) // BAD + _ = try Regex("((a*)*b)") // $ Alert // DUBIOUS (never used) [FLAGGED] + _ = try Regex("((a*)*b)").firstMatch(in: untainted) // $ Alert // DUBIOUS (never used on tainted input) [FLAGGED] + _ = try Regex("((a*)*b)").firstMatch(in: tainted) // $ Alert _ = try Regex(".*").firstMatch(in: tainted) // GOOD (safe regex) - let str = "((a*)*b)" // BAD + let str = "((a*)*b)" // $ Alert let regex = try Regex(str) _ = try regex.firstMatch(in: tainted) - _ = try Regex(#"(?is)X(?:.|\n)*Y"#) // BAD - suggested attack should begin with 'x' or 'X', *not* 'isx' or 'isX' + _ = try Regex(#"(?is)X(?:.|\n)*Y"#) // $ Alert // BAD - suggested attack should begin with 'x' or 'X', *not* 'isx' or 'isX' // NSRegularExpression - _ = try? NSRegularExpression(pattern: "((a*)*b)") // DUBIOUS (never used) [FLAGGED] + _ = try? NSRegularExpression(pattern: "((a*)*b)") // $ Alert // DUBIOUS (never used) [FLAGGED] - let nsregex1 = try? NSRegularExpression(pattern: "((a*)*b)") // DUBIOUS (never used on tainted input) [FLAGGED] + let nsregex1 = try? NSRegularExpression(pattern: "((a*)*b)") // $ Alert // DUBIOUS (never used on tainted input) [FLAGGED] _ = nsregex1?.stringByReplacingMatches(in: untainted, range: NSRange(location: 0, length: untainted.utf16.count), withTemplate: "") - let nsregex2 = try? NSRegularExpression(pattern: "((a*)*b)") // BAD + let nsregex2 = try? NSRegularExpression(pattern: "((a*)*b)") // $ Alert _ = nsregex2?.stringByReplacingMatches(in: tainted, range: NSRange(location: 0, length: tainted.utf16.count), withTemplate: "") let nsregex3 = try? NSRegularExpression(pattern: ".*") // GOOD (safe regex) diff --git a/swift/ql/test/query-tests/Security/CWE-134/UncontrolledFormatString.qlref b/swift/ql/test/query-tests/Security/CWE-134/UncontrolledFormatString.qlref index 115fef47e47..62b791e5d6f 100644 --- a/swift/ql/test/query-tests/Security/CWE-134/UncontrolledFormatString.qlref +++ b/swift/ql/test/query-tests/Security/CWE-134/UncontrolledFormatString.qlref @@ -1 +1,2 @@ -queries/Security/CWE-134/UncontrolledFormatString.ql \ No newline at end of file +query: queries/Security/CWE-134/UncontrolledFormatString.ql +postprocess: utils/test/InlineExpectationsTestQuery.ql diff --git a/swift/ql/test/query-tests/Security/CWE-134/UncontrolledFormatString.swift b/swift/ql/test/query-tests/Security/CWE-134/UncontrolledFormatString.swift index 2e3b082c63e..37c9b2bbca5 100644 --- a/swift/ql/test/query-tests/Security/CWE-134/UncontrolledFormatString.swift +++ b/swift/ql/test/query-tests/Security/CWE-134/UncontrolledFormatString.swift @@ -76,7 +76,7 @@ func vasprintf_l(_ ret: UnsafeMutablePointer?>?, _ l func MyLog(_ format: String, _ args: CVarArg...) { withVaList(args) { arglist in - NSLogv(format, arglist) // BAD + NSLogv(format, arglist) // $ Alert } } @@ -88,34 +88,34 @@ class MyString { } func tests() throws { - let tainted = try! String(contentsOf: URL(string: "http://example.com")!) + let tainted = try! String(contentsOf: URL(string: "http://example.com")!) // $ Source _ = String("abc") // GOOD: not a format string _ = String(tainted) // GOOD: not a format string _ = String(format: "abc") // GOOD: not tainted - _ = String(format: tainted) // BAD + _ = String(format: tainted) // $ Alert _ = String(format: "%s", "abc") // GOOD: not tainted _ = String(format: "%s", tainted) // GOOD: format string itself is not tainted - _ = String(format: tainted, "abc") // BAD - _ = String(format: tainted, tainted) // BAD + _ = String(format: tainted, "abc") // $ Alert + _ = String(format: tainted, tainted) // $ Alert - _ = String(format: tainted, arguments: []) // BAD - _ = String(format: tainted, locale: nil) // BAD - _ = String(format: tainted, locale: nil, arguments: []) // BAD - _ = String.localizedStringWithFormat(tainted) // BAD + _ = String(format: tainted, arguments: []) // $ Alert + _ = String(format: tainted, locale: nil) // $ Alert + _ = String(format: tainted, locale: nil, arguments: []) // $ Alert + _ = String.localizedStringWithFormat(tainted) // $ Alert - _ = NSString(format: NSString(string: tainted), "abc") // BAD - NSString.localizedStringWithFormat(NSString(string: tainted)) // BAD + _ = NSString(format: NSString(string: tainted), "abc") // $ Alert + NSString.localizedStringWithFormat(NSString(string: tainted)) // $ Alert - _ = NSMutableString(format: NSString(string: tainted), "abc") // BAD - NSMutableString.localizedStringWithFormat(NSString(string: tainted)) // BAD + _ = NSMutableString(format: NSString(string: tainted), "abc") // $ Alert + NSMutableString.localizedStringWithFormat(NSString(string: tainted)) // $ Alert NSLog("abc") // GOOD: not tainted - NSLog(tainted) // BAD - MyLog(tainted) // BAD + NSLog(tainted) // $ Alert + MyLog(tainted) // $ Alert - NSException.raise(NSExceptionName("exception"), format: tainted, arguments: getVaList([])) // BAD + NSException.raise(NSExceptionName("exception"), format: tainted, arguments: getVaList([])) // $ Alert let taintedVal = Int(tainted)! let taintedSan = "\(taintedVal)" @@ -127,32 +127,32 @@ func tests() throws { _ = String("abc").appendingFormat("%s", "abc") // GOOD: not tainted _ = String("abc").appendingFormat("%s", tainted) // GOOD: format not tainted - _ = String("abc").appendingFormat(tainted, "abc") // BAD + _ = String("abc").appendingFormat(tainted, "abc") // $ Alert _ = String(tainted).appendingFormat("%s", "abc") // GOOD: format not tainted let s = NSMutableString(string: "foo") s.appendFormat(NSString(string: "%s"), "abc") // GOOD: not tainted - s.appendFormat(NSString(string: tainted), "abc") // BAD + s.appendFormat(NSString(string: tainted), "abc") // $ Alert _ = NSPredicate(format: tainted) // GOOD: this should be flagged by `swift/predicate-injection`, not `swift/uncontrolled-format-string` tainted.withCString({ cstr in - _ = dprintf(0, cstr, "abc") // BAD + _ = dprintf(0, cstr, "abc") // $ Alert _ = dprintf(0, "%s", cstr) // GOOD: format not tainted - _ = vprintf(cstr, getVaList(["abc"])) // BAD + _ = vprintf(cstr, getVaList(["abc"])) // $ Alert _ = vprintf("%s", getVaList([cstr])) // GOOD: format not tainted - _ = vfprintf(nil, cstr, getVaList(["abc"])) // BAD + _ = vfprintf(nil, cstr, getVaList(["abc"])) // $ Alert _ = vfprintf(nil, "%s", getVaList([cstr])) // GOOD: format not tainted - _ = vasprintf_l(nil, nil, cstr, getVaList(["abc"])) // BAD + _ = vasprintf_l(nil, nil, cstr, getVaList(["abc"])) // $ Alert _ = vasprintf_l(nil, nil, "%s", getVaList([cstr])) // GOOD: format not tainted }) - myFormatMessage(string: tainted, "abc") // BAD [NOT DETECTED] + myFormatMessage(string: tainted, "abc") // $ MISSING: Alert // BAD [NOT DETECTED] myFormatMessage(string: "%s", tainted) // GOOD: format not tainted - _ = MyString(format: tainted, "abc") // BAD + _ = MyString(format: tainted, "abc") // $ Alert _ = MyString(format: "%s", tainted) // GOOD: format not tainted - _ = MyString(formatString: tainted, "abc") // BAD + _ = MyString(formatString: tainted, "abc") // $ Alert _ = MyString(formatString: "%s", tainted) // GOOD: format not tainted } diff --git a/swift/ql/test/query-tests/Security/CWE-259/ConstantPassword.qlref b/swift/ql/test/query-tests/Security/CWE-259/ConstantPassword.qlref index 0613f192631..57f452daecf 100644 --- a/swift/ql/test/query-tests/Security/CWE-259/ConstantPassword.qlref +++ b/swift/ql/test/query-tests/Security/CWE-259/ConstantPassword.qlref @@ -1 +1,2 @@ -queries/Security/CWE-259/ConstantPassword.ql +query: queries/Security/CWE-259/ConstantPassword.ql +postprocess: utils/test/InlineExpectationsTestQuery.ql diff --git a/swift/ql/test/query-tests/Security/CWE-259/rncryptor.swift b/swift/ql/test/query-tests/Security/CWE-259/rncryptor.swift index 6de5873c459..b115bb6750b 100644 --- a/swift/ql/test/query-tests/Security/CWE-259/rncryptor.swift +++ b/swift/ql/test/query-tests/Security/CWE-259/rncryptor.swift @@ -66,7 +66,7 @@ func test(cond: Bool) { let myData = Data(0) let myRandomPassword = getARandomPassword() - let myConstPassword = "abc123" + let myConstPassword = "abc123" // $ Source let myMaybePassword = cond ? myRandomPassword : myConstPassword // reasonable usage @@ -74,11 +74,11 @@ func test(cond: Bool) { let a = try? myEncryptor.encryptData(myData, with: kRNCryptorAES256Settings, password: myRandomPassword) // GOOD let _ = try? myDecryptor.decryptData(a, withPassword: myRandomPassword) // GOOD - let b = try? myEncryptor.encryptData(myData, with: kRNCryptorAES256Settings, password: myConstPassword) // BAD - let _ = try? myDecryptor.decryptData(b, withPassword: myConstPassword) // BAD + let b = try? myEncryptor.encryptData(myData, with: kRNCryptorAES256Settings, password: myConstPassword) // $ Alert + let _ = try? myDecryptor.decryptData(b, withPassword: myConstPassword) // $ Alert - let c = try? myEncryptor.encryptData(myData, with: kRNCryptorAES256Settings, password: myMaybePassword) // BAD - let _ = try? myDecryptor.decryptData(c, withPassword: myMaybePassword) // BAD + let c = try? myEncryptor.encryptData(myData, with: kRNCryptorAES256Settings, password: myMaybePassword) // $ Alert + let _ = try? myDecryptor.decryptData(c, withPassword: myMaybePassword) // $ Alert // all methods @@ -88,22 +88,22 @@ func test(cond: Bool) { let mySalt = Data(0) let mySalt2 = Data(0) - let _ = myEncryptor.key(forPassword: myConstPassword, salt: mySalt, settings: myKeyDerivationSettings) // BAD - let _ = myEncryptor.keyForPassword(myConstPassword, salt: mySalt, settings: myKeyDerivationSettings) // BAD - let _ = myDecryptor.key(forPassword: myConstPassword, salt: mySalt, settings: myKeyDerivationSettings) // BAD - let _ = myDecryptor.keyForPassword(myConstPassword, salt: mySalt, settings: myKeyDerivationSettings) // BAD + let _ = myEncryptor.key(forPassword: myConstPassword, salt: mySalt, settings: myKeyDerivationSettings) // $ Alert + let _ = myEncryptor.keyForPassword(myConstPassword, salt: mySalt, settings: myKeyDerivationSettings) // $ Alert + let _ = myDecryptor.key(forPassword: myConstPassword, salt: mySalt, settings: myKeyDerivationSettings) // $ Alert + let _ = myDecryptor.keyForPassword(myConstPassword, salt: mySalt, settings: myKeyDerivationSettings) // $ Alert - let _ = RNEncryptor(settings: kRNCryptorAES256Settings, password: myConstPassword, handler: myHandler) // BAD - let _ = RNEncryptor(settings: kRNCryptorAES256Settings, password: myConstPassword, iv: myIV, encryptionSalt: mySalt, hmacSalt: mySalt2, handler: myHandler) // BAD - let _ = RNEncryptor(settings: kRNCryptorAES256Settings, password: myConstPassword, IV: myIV, encryptionSalt: mySalt, HMACSalt: mySalt2, handler: myHandler) // BAD + let _ = RNEncryptor(settings: kRNCryptorAES256Settings, password: myConstPassword, handler: myHandler) // $ Alert + let _ = RNEncryptor(settings: kRNCryptorAES256Settings, password: myConstPassword, iv: myIV, encryptionSalt: mySalt, hmacSalt: mySalt2, handler: myHandler) // $ Alert + let _ = RNEncryptor(settings: kRNCryptorAES256Settings, password: myConstPassword, IV: myIV, encryptionSalt: mySalt, HMACSalt: mySalt2, handler: myHandler) // $ Alert - let _ = try? myEncryptor.encryptData(myData, with: kRNCryptorAES256Settings, password: myConstPassword) // BAD - let _ = try? myEncryptor.encryptData(myData, withSettings: kRNCryptorAES256Settings, password: myConstPassword) // BAD - let _ = try? myEncryptor.encryptData(myData, with: kRNCryptorAES256Settings, password: myConstPassword, iv: myIV, encryptionSalt: mySalt, hmacSalt: mySalt2) // BAD - let _ = try? myEncryptor.encryptData(myData, withSettings: kRNCryptorAES256Settings, password: myConstPassword, IV: myIV, encryptionSalt: mySalt, HMACSalt: mySalt2) // BAD + let _ = try? myEncryptor.encryptData(myData, with: kRNCryptorAES256Settings, password: myConstPassword) // $ Alert + let _ = try? myEncryptor.encryptData(myData, withSettings: kRNCryptorAES256Settings, password: myConstPassword) // $ Alert + let _ = try? myEncryptor.encryptData(myData, with: kRNCryptorAES256Settings, password: myConstPassword, iv: myIV, encryptionSalt: mySalt, hmacSalt: mySalt2) // $ Alert + let _ = try? myEncryptor.encryptData(myData, withSettings: kRNCryptorAES256Settings, password: myConstPassword, IV: myIV, encryptionSalt: mySalt, HMACSalt: mySalt2) // $ Alert - let _ = RNDecryptor(password: myConstPassword, handler: myHandler) // BAD + let _ = RNDecryptor(password: myConstPassword, handler: myHandler) // $ Alert - let _ = try? myDecryptor.decryptData(myData, withPassword: myConstPassword) // BAD - let _ = try? myDecryptor.decryptData(myData, withSettings: kRNCryptorAES256Settings, password: myConstPassword) // BAD + let _ = try? myDecryptor.decryptData(myData, withPassword: myConstPassword) // $ Alert + let _ = try? myDecryptor.decryptData(myData, withSettings: kRNCryptorAES256Settings, password: myConstPassword) // $ Alert } diff --git a/swift/ql/test/query-tests/Security/CWE-259/test.swift b/swift/ql/test/query-tests/Security/CWE-259/test.swift index 923c49bffbd..da657b95b6a 100644 --- a/swift/ql/test/query-tests/Security/CWE-259/test.swift +++ b/swift/ql/test/query-tests/Security/CWE-259/test.swift @@ -26,7 +26,7 @@ final class Scrypt { // Helper functions func getConstantString() -> String { - "this string is constant" + "this string is constant" // $ Source } func getConstantArray() -> Array { @@ -40,7 +40,7 @@ func getRandomArray() -> Array { // --- tests --- func test() { - let constantPassword: Array = [0x2a, 0x3a, 0x80, 0x05, 0xaf, 0x46, 0x58, 0x2d, 0x66, 0x52, 0x10, 0xae, 0x86, 0xd3, 0x8e, 0x8f] + let constantPassword: Array = [0x2a, 0x3a, 0x80, 0x05, 0xaf, 0x46, 0x58, 0x2d, 0x66, 0x52, 0x10, 0xae, 0x86, 0xd3, 0x8e, 0x8f] // $ Source let constantStringPassword = getConstantArray() let randomPassword = getRandomArray() let randomArray = getRandomArray() @@ -48,23 +48,23 @@ func test() { let iterations = 120120 // HKDF test cases - let hkdfb1 = HKDF(password: constantPassword, salt: randomArray, info: randomArray, keyLength: 0, variant: variant) // BAD - let hkdfb2 = HKDF(password: constantStringPassword, salt: randomArray, info: randomArray, keyLength: 0, variant: variant) // BAD + let hkdfb1 = HKDF(password: constantPassword, salt: randomArray, info: randomArray, keyLength: 0, variant: variant) // $ Alert + let hkdfb2 = HKDF(password: constantStringPassword, salt: randomArray, info: randomArray, keyLength: 0, variant: variant) // $ Alert let hkdfg1 = HKDF(password: randomPassword, salt: randomArray, info: randomArray, keyLength: 0, variant: variant) // GOOD // PBKDF1 test cases - let pbkdf1b1 = PKCS5.PBKDF1(password: constantPassword, salt: randomArray, iterations: iterations, keyLength: 0) // BAD - let pbkdf1b2 = PKCS5.PBKDF1(password: constantStringPassword, salt: randomArray, iterations: iterations, keyLength: 0) // BAD + let pbkdf1b1 = PKCS5.PBKDF1(password: constantPassword, salt: randomArray, iterations: iterations, keyLength: 0) // $ Alert + let pbkdf1b2 = PKCS5.PBKDF1(password: constantStringPassword, salt: randomArray, iterations: iterations, keyLength: 0) // $ Alert let pbkdf1g1 = PKCS5.PBKDF1(password: randomPassword, salt: randomArray, iterations: iterations, keyLength: 0) // GOOD // PBKDF2 test cases - let pbkdf2b1 = PKCS5.PBKDF2(password: constantPassword, salt: randomArray, iterations: iterations, keyLength: 0) // BAD - let pbkdf2b2 = PKCS5.PBKDF2(password: constantStringPassword, salt: randomArray, iterations: iterations, keyLength: 0) // BAD + let pbkdf2b1 = PKCS5.PBKDF2(password: constantPassword, salt: randomArray, iterations: iterations, keyLength: 0) // $ Alert + let pbkdf2b2 = PKCS5.PBKDF2(password: constantStringPassword, salt: randomArray, iterations: iterations, keyLength: 0) // $ Alert let pbkdf2g1 = PKCS5.PBKDF2(password: randomPassword, salt: randomArray, iterations: iterations, keyLength: 0) // GOOD // Scrypt test cases - let scryptb1 = Scrypt(password: constantPassword, salt: randomArray, dkLen: 64, N: 16384, r: 8, p: 1) // BAD - let scryptb2 = Scrypt(password: constantStringPassword, salt: randomArray, dkLen: 64, N: 16384, r: 8, p: 1) // BAD + let scryptb1 = Scrypt(password: constantPassword, salt: randomArray, dkLen: 64, N: 16384, r: 8, p: 1) // $ Alert + let scryptb2 = Scrypt(password: constantStringPassword, salt: randomArray, dkLen: 64, N: 16384, r: 8, p: 1) // $ Alert let scryptg1 = Scrypt(password: randomPassword, salt: randomArray, dkLen: 64, N: 16384, r: 8, p: 1) // GOOD } diff --git a/swift/ql/test/query-tests/Security/CWE-311/CleartextStorageDatabase.expected b/swift/ql/test/query-tests/Security/CWE-311/CleartextStorageDatabase.expected index 204e2486cc2..e3517d64826 100644 --- a/swift/ql/test/query-tests/Security/CWE-311/CleartextStorageDatabase.expected +++ b/swift/ql/test/query-tests/Security/CWE-311/CleartextStorageDatabase.expected @@ -1,3 +1,143 @@ +#select +| SQLite.swift:123:17:123:17 | insertQuery | SQLite.swift:119:70:119:70 | mobilePhoneNumber | SQLite.swift:123:17:123:17 | insertQuery | This operation stores 'insertQuery' in a database. It may contain unencrypted sensitive data from $@. | SQLite.swift:119:70:119:70 | mobilePhoneNumber | mobilePhoneNumber | +| SQLite.swift:124:17:124:17 | updateQuery | SQLite.swift:120:50:120:50 | mobilePhoneNumber | SQLite.swift:124:17:124:17 | updateQuery | This operation stores 'updateQuery' in a database. It may contain unencrypted sensitive data from $@. | SQLite.swift:120:50:120:50 | mobilePhoneNumber | mobilePhoneNumber | +| SQLite.swift:127:21:127:21 | insertQuery | SQLite.swift:119:70:119:70 | mobilePhoneNumber | SQLite.swift:127:21:127:21 | insertQuery | This operation stores 'insertQuery' in a database. It may contain unencrypted sensitive data from $@. | SQLite.swift:119:70:119:70 | mobilePhoneNumber | mobilePhoneNumber | +| SQLite.swift:128:21:128:21 | updateQuery | SQLite.swift:120:50:120:50 | mobilePhoneNumber | SQLite.swift:128:21:128:21 | updateQuery | This operation stores 'updateQuery' in a database. It may contain unencrypted sensitive data from $@. | SQLite.swift:120:50:120:50 | mobilePhoneNumber | mobilePhoneNumber | +| SQLite.swift:131:17:131:17 | insertQuery | SQLite.swift:119:70:119:70 | mobilePhoneNumber | SQLite.swift:131:17:131:17 | insertQuery | This operation stores 'insertQuery' in a database. It may contain unencrypted sensitive data from $@. | SQLite.swift:119:70:119:70 | mobilePhoneNumber | mobilePhoneNumber | +| SQLite.swift:132:17:132:17 | updateQuery | SQLite.swift:120:50:120:50 | mobilePhoneNumber | SQLite.swift:132:17:132:17 | updateQuery | This operation stores 'updateQuery' in a database. It may contain unencrypted sensitive data from $@. | SQLite.swift:120:50:120:50 | mobilePhoneNumber | mobilePhoneNumber | +| SQLite.swift:135:20:135:20 | insertQuery | SQLite.swift:119:70:119:70 | mobilePhoneNumber | SQLite.swift:135:20:135:20 | insertQuery | This operation stores 'insertQuery' in a database. It may contain unencrypted sensitive data from $@. | SQLite.swift:119:70:119:70 | mobilePhoneNumber | mobilePhoneNumber | +| SQLite.swift:136:20:136:20 | updateQuery | SQLite.swift:120:50:120:50 | mobilePhoneNumber | SQLite.swift:136:20:136:20 | updateQuery | This operation stores 'updateQuery' in a database. It may contain unencrypted sensitive data from $@. | SQLite.swift:120:50:120:50 | mobilePhoneNumber | mobilePhoneNumber | +| SQLite.swift:139:24:139:24 | insertQuery | SQLite.swift:119:70:119:70 | mobilePhoneNumber | SQLite.swift:139:24:139:24 | insertQuery | This operation stores 'insertQuery' in a database. It may contain unencrypted sensitive data from $@. | SQLite.swift:119:70:119:70 | mobilePhoneNumber | mobilePhoneNumber | +| SQLite.swift:140:24:140:24 | updateQuery | SQLite.swift:120:50:120:50 | mobilePhoneNumber | SQLite.swift:140:24:140:24 | updateQuery | This operation stores 'updateQuery' in a database. It may contain unencrypted sensitive data from $@. | SQLite.swift:120:50:120:50 | mobilePhoneNumber | mobilePhoneNumber | +| SQLite.swift:147:32:147:32 | [...] | SQLite.swift:147:32:147:32 | mobilePhoneNumber | SQLite.swift:147:32:147:32 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | SQLite.swift:147:32:147:32 | mobilePhoneNumber | mobilePhoneNumber | +| SQLite.swift:148:28:148:28 | [...] | SQLite.swift:148:28:148:28 | mobilePhoneNumber | SQLite.swift:148:28:148:28 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | SQLite.swift:148:28:148:28 | mobilePhoneNumber | mobilePhoneNumber | +| SQLite.swift:149:31:149:31 | [...] | SQLite.swift:149:31:149:31 | mobilePhoneNumber | SQLite.swift:149:31:149:31 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | SQLite.swift:149:31:149:31 | mobilePhoneNumber | mobilePhoneNumber | +| SQLite.swift:152:21:152:21 | [...] | SQLite.swift:152:21:152:21 | mobilePhoneNumber | SQLite.swift:152:21:152:21 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | SQLite.swift:152:21:152:21 | mobilePhoneNumber | mobilePhoneNumber | +| SQLite.swift:153:20:153:20 | [...] | SQLite.swift:153:20:153:20 | mobilePhoneNumber | SQLite.swift:153:20:153:20 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | SQLite.swift:153:20:153:20 | mobilePhoneNumber | mobilePhoneNumber | +| SQLite.swift:154:23:154:23 | [...] | SQLite.swift:154:23:154:23 | mobilePhoneNumber | SQLite.swift:154:23:154:23 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | SQLite.swift:154:23:154:23 | mobilePhoneNumber | mobilePhoneNumber | +| SQLite.swift:158:32:158:54 | [...] | SQLite.swift:158:33:158:33 | mobilePhoneNumber | SQLite.swift:158:32:158:54 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | SQLite.swift:158:33:158:33 | mobilePhoneNumber | mobilePhoneNumber | +| SQLite.swift:159:28:159:50 | [...] | SQLite.swift:159:29:159:29 | mobilePhoneNumber | SQLite.swift:159:28:159:50 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | SQLite.swift:159:29:159:29 | mobilePhoneNumber | mobilePhoneNumber | +| SQLite.swift:160:31:160:53 | [...] | SQLite.swift:160:32:160:32 | mobilePhoneNumber | SQLite.swift:160:31:160:53 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | SQLite.swift:160:32:160:32 | mobilePhoneNumber | mobilePhoneNumber | +| SQLite.swift:163:21:163:43 | [...] | SQLite.swift:163:22:163:22 | mobilePhoneNumber | SQLite.swift:163:21:163:43 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | SQLite.swift:163:22:163:22 | mobilePhoneNumber | mobilePhoneNumber | +| SQLite.swift:164:20:164:42 | [...] | SQLite.swift:164:21:164:21 | mobilePhoneNumber | SQLite.swift:164:20:164:42 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | SQLite.swift:164:21:164:21 | mobilePhoneNumber | mobilePhoneNumber | +| SQLite.swift:165:23:165:45 | [...] | SQLite.swift:165:24:165:24 | mobilePhoneNumber | SQLite.swift:165:23:165:45 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | SQLite.swift:165:24:165:24 | mobilePhoneNumber | mobilePhoneNumber | +| SQLite.swift:169:32:169:70 | [...] | SQLite.swift:169:53:169:53 | mobilePhoneNumber | SQLite.swift:169:32:169:70 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | SQLite.swift:169:53:169:53 | mobilePhoneNumber | mobilePhoneNumber | +| SQLite.swift:170:28:170:66 | [...] | SQLite.swift:170:49:170:49 | mobilePhoneNumber | SQLite.swift:170:28:170:66 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | SQLite.swift:170:49:170:49 | mobilePhoneNumber | mobilePhoneNumber | +| SQLite.swift:171:31:171:69 | [...] | SQLite.swift:171:52:171:52 | mobilePhoneNumber | SQLite.swift:171:31:171:69 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | SQLite.swift:171:52:171:52 | mobilePhoneNumber | mobilePhoneNumber | +| SQLite.swift:174:21:174:59 | [...] | SQLite.swift:174:42:174:42 | mobilePhoneNumber | SQLite.swift:174:21:174:59 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | SQLite.swift:174:42:174:42 | mobilePhoneNumber | mobilePhoneNumber | +| SQLite.swift:175:20:175:58 | [...] | SQLite.swift:175:41:175:41 | mobilePhoneNumber | SQLite.swift:175:20:175:58 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | SQLite.swift:175:41:175:41 | mobilePhoneNumber | mobilePhoneNumber | +| SQLite.swift:176:23:176:61 | [...] | SQLite.swift:176:44:176:44 | mobilePhoneNumber | SQLite.swift:176:23:176:61 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | SQLite.swift:176:44:176:44 | mobilePhoneNumber | mobilePhoneNumber | +| SQLite.swift:186:40:186:54 | [...] | SQLite.swift:186:54:186:54 | mobilePhoneNumber | SQLite.swift:186:40:186:54 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | SQLite.swift:186:54:186:54 | mobilePhoneNumber | mobilePhoneNumber | +| SQLite.swift:189:26:189:40 | [...] | SQLite.swift:189:40:189:40 | mobilePhoneNumber | SQLite.swift:189:26:189:40 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | SQLite.swift:189:40:189:40 | mobilePhoneNumber | mobilePhoneNumber | +| SQLite.swift:191:27:191:41 | [...] | SQLite.swift:191:41:191:41 | mobilePhoneNumber | SQLite.swift:191:27:191:41 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | SQLite.swift:191:41:191:41 | mobilePhoneNumber | mobilePhoneNumber | +| SQLite.swift:193:26:193:89 | [...] | SQLite.swift:193:72:193:72 | mobilePhoneNumber | SQLite.swift:193:26:193:89 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | SQLite.swift:193:72:193:72 | mobilePhoneNumber | mobilePhoneNumber | +| SQLite.swift:199:30:199:30 | badMany | SQLite.swift:197:32:197:32 | mobilePhoneNumber | SQLite.swift:199:30:199:30 | badMany | This operation stores 'badMany' in a database. It may contain unencrypted sensitive data from $@. | SQLite.swift:197:32:197:32 | mobilePhoneNumber | mobilePhoneNumber | +| SQLite.swift:201:54:201:54 | badMany | SQLite.swift:197:32:197:32 | mobilePhoneNumber | SQLite.swift:201:54:201:54 | badMany | This operation stores 'badMany' in a database. It may contain unencrypted sensitive data from $@. | SQLite.swift:197:32:197:32 | mobilePhoneNumber | mobilePhoneNumber | +| sqlite3_c_api.swift:46:27:46:27 | insertQuery | sqlite3_c_api.swift:42:69:42:69 | medicalNotes | sqlite3_c_api.swift:46:27:46:27 | insertQuery | This operation stores 'insertQuery' in a database. It may contain unencrypted sensitive data from $@. | sqlite3_c_api.swift:42:69:42:69 | medicalNotes | medicalNotes | +| sqlite3_c_api.swift:47:27:47:27 | updateQuery | sqlite3_c_api.swift:43:49:43:49 | medicalNotes | sqlite3_c_api.swift:47:27:47:27 | updateQuery | This operation stores 'updateQuery' in a database. It may contain unencrypted sensitive data from $@. | sqlite3_c_api.swift:43:49:43:49 | medicalNotes | medicalNotes | +| sqlite3_c_api.swift:58:36:58:36 | medicalNotes | sqlite3_c_api.swift:58:36:58:36 | medicalNotes | sqlite3_c_api.swift:58:36:58:36 | medicalNotes | This operation stores 'medicalNotes' in a database. It may contain unencrypted sensitive data from $@. | sqlite3_c_api.swift:58:36:58:36 | medicalNotes | medicalNotes | +| testCoreData2.swift:37:2:37:2 | obj | testCoreData2.swift:37:16:37:16 | bankAccountNo | testCoreData2.swift:37:2:37:2 | [post] obj | This operation stores 'obj' in a database. It may contain unencrypted sensitive data from $@. | testCoreData2.swift:37:16:37:16 | bankAccountNo | bankAccountNo | +| testCoreData2.swift:39:2:39:2 | obj | testCoreData2.swift:39:28:39:28 | bankAccountNo | testCoreData2.swift:39:2:39:2 | [post] obj | This operation stores 'obj' in a database. It may contain unencrypted sensitive data from $@. | testCoreData2.swift:39:28:39:28 | bankAccountNo | bankAccountNo | +| testCoreData2.swift:41:2:41:2 | obj | testCoreData2.swift:41:29:41:29 | bankAccountNo | testCoreData2.swift:41:2:41:2 | [post] obj | This operation stores 'obj' in a database. It may contain unencrypted sensitive data from $@. | testCoreData2.swift:41:29:41:29 | bankAccountNo | bankAccountNo | +| testCoreData2.swift:43:2:43:2 | obj | testCoreData2.swift:43:35:43:35 | bankAccountNo | testCoreData2.swift:43:2:43:2 | [post] obj | This operation stores 'obj' in a database. It may contain unencrypted sensitive data from $@. | testCoreData2.swift:43:35:43:35 | bankAccountNo | bankAccountNo | +| testCoreData2.swift:46:2:46:10 | ...? | testCoreData2.swift:46:22:46:22 | bankAccountNo | testCoreData2.swift:46:2:46:10 | [post] ...? | This operation stores '...?' in a database. It may contain unencrypted sensitive data from $@. | testCoreData2.swift:46:22:46:22 | bankAccountNo | bankAccountNo | +| testCoreData2.swift:48:2:48:10 | ...? | testCoreData2.swift:48:34:48:34 | bankAccountNo | testCoreData2.swift:48:2:48:10 | [post] ...? | This operation stores '...?' in a database. It may contain unencrypted sensitive data from $@. | testCoreData2.swift:48:34:48:34 | bankAccountNo | bankAccountNo | +| testCoreData2.swift:50:2:50:10 | ...? | testCoreData2.swift:50:35:50:35 | bankAccountNo | testCoreData2.swift:50:2:50:10 | [post] ...? | This operation stores '...?' in a database. It may contain unencrypted sensitive data from $@. | testCoreData2.swift:50:35:50:35 | bankAccountNo | bankAccountNo | +| testCoreData2.swift:52:2:52:10 | ...? | testCoreData2.swift:52:41:52:41 | bankAccountNo | testCoreData2.swift:52:2:52:10 | [post] ...? | This operation stores '...?' in a database. It may contain unencrypted sensitive data from $@. | testCoreData2.swift:52:41:52:41 | bankAccountNo | bankAccountNo | +| testCoreData2.swift:57:3:57:3 | obj | testCoreData2.swift:57:29:57:29 | bankAccountNo | testCoreData2.swift:57:3:57:3 | [post] obj | This operation stores 'obj' in a database. It may contain unencrypted sensitive data from $@. | testCoreData2.swift:57:29:57:29 | bankAccountNo | bankAccountNo | +| testCoreData2.swift:60:4:60:4 | obj | testCoreData2.swift:60:30:60:30 | bankAccountNo | testCoreData2.swift:60:4:60:4 | [post] obj | This operation stores 'obj' in a database. It may contain unencrypted sensitive data from $@. | testCoreData2.swift:60:30:60:30 | bankAccountNo | bankAccountNo | +| testCoreData2.swift:62:4:62:4 | obj | testCoreData2.swift:62:30:62:30 | bankAccountNo | testCoreData2.swift:62:4:62:4 | [post] obj | This operation stores 'obj' in a database. It may contain unencrypted sensitive data from $@. | testCoreData2.swift:62:30:62:30 | bankAccountNo | bankAccountNo | +| testCoreData2.swift:65:3:65:3 | obj | testCoreData2.swift:65:29:65:29 | bankAccountNo | testCoreData2.swift:65:3:65:3 | [post] obj | This operation stores 'obj' in a database. It may contain unencrypted sensitive data from $@. | testCoreData2.swift:65:29:65:29 | bankAccountNo | bankAccountNo | +| testCoreData2.swift:79:2:79:2 | dbObj | testCoreData2.swift:79:18:79:28 | .bankAccountNo | testCoreData2.swift:79:2:79:2 | [post] dbObj | This operation stores 'dbObj' in a database. It may contain unencrypted sensitive data from $@. | testCoreData2.swift:79:18:79:28 | .bankAccountNo | .bankAccountNo | +| testCoreData2.swift:80:2:80:2 | dbObj | testCoreData2.swift:80:18:80:28 | .bankAccountNo2 | testCoreData2.swift:80:2:80:2 | [post] dbObj | This operation stores 'dbObj' in a database. It may contain unencrypted sensitive data from $@. | testCoreData2.swift:80:18:80:28 | .bankAccountNo2 | .bankAccountNo2 | +| testCoreData2.swift:82:2:82:2 | dbObj | testCoreData2.swift:82:18:82:18 | bankAccountNo | testCoreData2.swift:82:2:82:2 | [post] dbObj | This operation stores 'dbObj' in a database. It may contain unencrypted sensitive data from $@. | testCoreData2.swift:82:18:82:18 | bankAccountNo | bankAccountNo | +| testCoreData2.swift:83:2:83:2 | dbObj | testCoreData2.swift:83:18:83:18 | bankAccountNo | testCoreData2.swift:83:2:83:2 | [post] dbObj | This operation stores 'dbObj' in a database. It may contain unencrypted sensitive data from $@. | testCoreData2.swift:83:18:83:18 | bankAccountNo | bankAccountNo | +| testCoreData2.swift:84:2:84:2 | dbObj | testCoreData2.swift:84:18:84:18 | bankAccountNo2 | testCoreData2.swift:84:2:84:2 | [post] dbObj | This operation stores 'dbObj' in a database. It may contain unencrypted sensitive data from $@. | testCoreData2.swift:84:18:84:18 | bankAccountNo2 | bankAccountNo2 | +| testCoreData2.swift:85:2:85:2 | dbObj | testCoreData2.swift:85:18:85:18 | bankAccountNo2 | testCoreData2.swift:85:2:85:2 | [post] dbObj | This operation stores 'dbObj' in a database. It may contain unencrypted sensitive data from $@. | testCoreData2.swift:85:18:85:18 | bankAccountNo2 | bankAccountNo2 | +| testCoreData2.swift:87:2:87:10 | ...? | testCoreData2.swift:87:22:87:32 | .bankAccountNo | testCoreData2.swift:87:2:87:10 | [post] ...? | This operation stores '...?' in a database. It may contain unencrypted sensitive data from $@. | testCoreData2.swift:87:22:87:32 | .bankAccountNo | .bankAccountNo | +| testCoreData2.swift:88:2:88:10 | ...? | testCoreData2.swift:88:22:88:22 | bankAccountNo | testCoreData2.swift:88:2:88:10 | [post] ...? | This operation stores '...?' in a database. It may contain unencrypted sensitive data from $@. | testCoreData2.swift:88:22:88:22 | bankAccountNo | bankAccountNo | +| testCoreData2.swift:89:2:89:10 | ...? | testCoreData2.swift:89:22:89:22 | bankAccountNo2 | testCoreData2.swift:89:2:89:10 | [post] ...? | This operation stores '...?' in a database. It may contain unencrypted sensitive data from $@. | testCoreData2.swift:89:22:89:22 | bankAccountNo2 | bankAccountNo2 | +| testCoreData2.swift:93:2:93:2 | dbObj | testCoreData2.swift:91:10:91:10 | bankAccountNo | testCoreData2.swift:93:2:93:2 | [post] dbObj | This operation stores 'dbObj' in a database. It may contain unencrypted sensitive data from $@. | testCoreData2.swift:91:10:91:10 | bankAccountNo | bankAccountNo | +| testCoreData2.swift:98:2:98:2 | dbObj | testCoreData2.swift:95:10:95:10 | bankAccountNo | testCoreData2.swift:98:2:98:2 | [post] dbObj | This operation stores 'dbObj' in a database. It may contain unencrypted sensitive data from $@. | testCoreData2.swift:95:10:95:10 | bankAccountNo | bankAccountNo | +| testCoreData2.swift:104:2:104:2 | dbObj | testCoreData2.swift:101:10:101:10 | bankAccountNo | testCoreData2.swift:104:2:104:2 | [post] dbObj | This operation stores 'dbObj' in a database. It may contain unencrypted sensitive data from $@. | testCoreData2.swift:101:10:101:10 | bankAccountNo | bankAccountNo | +| testCoreData2.swift:105:2:105:2 | dbObj | testCoreData2.swift:101:10:101:10 | bankAccountNo | testCoreData2.swift:105:2:105:2 | [post] dbObj | This operation stores 'dbObj' in a database. It may contain unencrypted sensitive data from $@. | testCoreData2.swift:101:10:101:10 | bankAccountNo | bankAccountNo | +| testCoreData.swift:19:12:19:12 | value | testCoreData.swift:61:25:61:25 | password | testCoreData.swift:19:12:19:12 | value | This operation stores 'value' in a database. It may contain unencrypted sensitive data from $@. | testCoreData.swift:61:25:61:25 | password | password | +| testCoreData.swift:32:13:32:13 | newValue | testCoreData.swift:64:16:64:16 | password | testCoreData.swift:32:13:32:13 | newValue | This operation stores 'newValue' in a database. It may contain unencrypted sensitive data from $@. | testCoreData.swift:64:16:64:16 | password | password | +| testCoreData.swift:48:15:48:15 | password | testCoreData.swift:48:15:48:15 | password | testCoreData.swift:48:15:48:15 | password | This operation stores 'password' in a database. It may contain unencrypted sensitive data from $@. | testCoreData.swift:48:15:48:15 | password | password | +| testCoreData.swift:51:24:51:24 | password | testCoreData.swift:51:24:51:24 | password | testCoreData.swift:51:24:51:24 | password | This operation stores 'password' in a database. It may contain unencrypted sensitive data from $@. | testCoreData.swift:51:24:51:24 | password | password | +| testCoreData.swift:58:15:58:15 | password | testCoreData.swift:58:15:58:15 | password | testCoreData.swift:58:15:58:15 | password | This operation stores 'password' in a database. It may contain unencrypted sensitive data from $@. | testCoreData.swift:58:15:58:15 | password | password | +| testCoreData.swift:64:2:64:2 | obj | testCoreData.swift:64:16:64:16 | password | testCoreData.swift:64:2:64:2 | [post] obj | This operation stores 'obj' in a database. It may contain unencrypted sensitive data from $@. | testCoreData.swift:64:16:64:16 | password | password | +| testCoreData.swift:78:15:78:15 | x | testCoreData.swift:77:24:77:24 | x | testCoreData.swift:78:15:78:15 | x | This operation stores 'x' in a database. It may contain unencrypted sensitive data from $@. | testCoreData.swift:77:24:77:24 | x | x | +| testCoreData.swift:81:15:81:15 | y | testCoreData.swift:80:10:80:22 | call to getPassword() | testCoreData.swift:81:15:81:15 | y | This operation stores 'y' in a database. It may contain unencrypted sensitive data from $@. | testCoreData.swift:80:10:80:22 | call to getPassword() | call to getPassword() | +| testCoreData.swift:85:15:85:17 | .password | testCoreData.swift:85:15:85:17 | .password | testCoreData.swift:85:15:85:17 | .password | This operation stores '.password' in a database. It may contain unencrypted sensitive data from $@. | testCoreData.swift:85:15:85:17 | .password | .password | +| testCoreData.swift:95:15:95:15 | x | testCoreData.swift:91:10:91:10 | passwd | testCoreData.swift:95:15:95:15 | x | This operation stores 'x' in a database. It may contain unencrypted sensitive data from $@. | testCoreData.swift:91:10:91:10 | passwd | passwd | +| testCoreData.swift:96:15:96:15 | y | testCoreData.swift:92:10:92:10 | passwd | testCoreData.swift:96:15:96:15 | y | This operation stores 'y' in a database. It may contain unencrypted sensitive data from $@. | testCoreData.swift:92:10:92:10 | passwd | passwd | +| testCoreData.swift:97:15:97:15 | z | testCoreData.swift:93:10:93:10 | passwd | testCoreData.swift:97:15:97:15 | z | This operation stores 'z' in a database. It may contain unencrypted sensitive data from $@. | testCoreData.swift:93:10:93:10 | passwd | passwd | +| testCoreData.swift:128:15:128:33 | call to generateSecretKey() | testCoreData.swift:128:15:128:33 | call to generateSecretKey() | testCoreData.swift:128:15:128:33 | call to generateSecretKey() | This operation stores 'call to generateSecretKey()' in a database. It may contain unencrypted sensitive data from $@. | testCoreData.swift:128:15:128:33 | call to generateSecretKey() | call to generateSecretKey() | +| testCoreData.swift:129:15:129:30 | call to getCertificate() | testCoreData.swift:129:15:129:30 | call to getCertificate() | testCoreData.swift:129:15:129:30 | call to getCertificate() | This operation stores 'call to getCertificate()' in a database. It may contain unencrypted sensitive data from $@. | testCoreData.swift:129:15:129:30 | call to getCertificate() | call to getCertificate() | +| testGRDB.swift:73:56:73:65 | [...] | testGRDB.swift:73:57:73:57 | password | testGRDB.swift:73:56:73:65 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:73:57:73:57 | password | password | +| testGRDB.swift:76:42:76:51 | [...] | testGRDB.swift:76:43:76:43 | password | testGRDB.swift:76:42:76:51 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:76:43:76:43 | password | password | +| testGRDB.swift:81:44:81:53 | [...] | testGRDB.swift:81:45:81:45 | password | testGRDB.swift:81:44:81:53 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:81:45:81:45 | password | password | +| testGRDB.swift:83:44:83:53 | [...] | testGRDB.swift:83:45:83:45 | password | testGRDB.swift:83:44:83:53 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:83:45:83:45 | password | password | +| testGRDB.swift:85:44:85:53 | [...] | testGRDB.swift:85:45:85:45 | password | testGRDB.swift:85:44:85:53 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:85:45:85:45 | password | password | +| testGRDB.swift:87:44:87:53 | [...] | testGRDB.swift:87:45:87:45 | password | testGRDB.swift:87:44:87:53 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:87:45:87:45 | password | password | +| testGRDB.swift:92:37:92:46 | [...] | testGRDB.swift:92:38:92:38 | password | testGRDB.swift:92:37:92:46 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:92:38:92:38 | password | password | +| testGRDB.swift:95:36:95:45 | [...] | testGRDB.swift:95:37:95:37 | password | testGRDB.swift:95:36:95:45 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:95:37:95:37 | password | password | +| testGRDB.swift:100:72:100:81 | [...] | testGRDB.swift:100:73:100:73 | password | testGRDB.swift:100:72:100:81 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:100:73:100:73 | password | password | +| testGRDB.swift:101:72:101:81 | [...] | testGRDB.swift:101:73:101:73 | password | testGRDB.swift:101:72:101:81 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:101:73:101:73 | password | password | +| testGRDB.swift:107:52:107:61 | [...] | testGRDB.swift:107:53:107:53 | password | testGRDB.swift:107:52:107:61 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:107:53:107:53 | password | password | +| testGRDB.swift:109:52:109:61 | [...] | testGRDB.swift:109:53:109:53 | password | testGRDB.swift:109:52:109:61 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:109:53:109:53 | password | password | +| testGRDB.swift:111:51:111:60 | [...] | testGRDB.swift:111:52:111:52 | password | testGRDB.swift:111:51:111:60 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:111:52:111:52 | password | password | +| testGRDB.swift:116:47:116:56 | [...] | testGRDB.swift:116:48:116:48 | password | testGRDB.swift:116:47:116:56 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:116:48:116:48 | password | password | +| testGRDB.swift:118:47:118:56 | [...] | testGRDB.swift:118:48:118:48 | password | testGRDB.swift:118:47:118:56 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:118:48:118:48 | password | password | +| testGRDB.swift:121:44:121:53 | [...] | testGRDB.swift:121:45:121:45 | password | testGRDB.swift:121:44:121:53 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:121:45:121:45 | password | password | +| testGRDB.swift:123:44:123:53 | [...] | testGRDB.swift:123:45:123:45 | password | testGRDB.swift:123:44:123:53 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:123:45:123:45 | password | password | +| testGRDB.swift:126:44:126:53 | [...] | testGRDB.swift:126:45:126:45 | password | testGRDB.swift:126:44:126:53 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:126:45:126:45 | password | password | +| testGRDB.swift:128:44:128:53 | [...] | testGRDB.swift:128:45:128:45 | password | testGRDB.swift:128:44:128:53 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:128:45:128:45 | password | password | +| testGRDB.swift:131:44:131:53 | [...] | testGRDB.swift:131:45:131:45 | password | testGRDB.swift:131:44:131:53 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:131:45:131:45 | password | password | +| testGRDB.swift:133:44:133:53 | [...] | testGRDB.swift:133:45:133:45 | password | testGRDB.swift:133:44:133:53 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:133:45:133:45 | password | password | +| testGRDB.swift:138:68:138:77 | [...] | testGRDB.swift:138:69:138:69 | password | testGRDB.swift:138:68:138:77 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:138:69:138:69 | password | password | +| testGRDB.swift:140:68:140:77 | [...] | testGRDB.swift:140:69:140:69 | password | testGRDB.swift:140:68:140:77 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:140:69:140:69 | password | password | +| testGRDB.swift:143:65:143:74 | [...] | testGRDB.swift:143:66:143:66 | password | testGRDB.swift:143:65:143:74 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:143:66:143:66 | password | password | +| testGRDB.swift:145:65:145:74 | [...] | testGRDB.swift:145:66:145:66 | password | testGRDB.swift:145:65:145:74 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:145:66:145:66 | password | password | +| testGRDB.swift:148:65:148:74 | [...] | testGRDB.swift:148:66:148:66 | password | testGRDB.swift:148:65:148:74 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:148:66:148:66 | password | password | +| testGRDB.swift:150:65:150:74 | [...] | testGRDB.swift:150:66:150:66 | password | testGRDB.swift:150:65:150:74 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:150:66:150:66 | password | password | +| testGRDB.swift:153:65:153:74 | [...] | testGRDB.swift:153:66:153:66 | password | testGRDB.swift:153:65:153:74 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:153:66:153:66 | password | password | +| testGRDB.swift:155:65:155:74 | [...] | testGRDB.swift:155:66:155:66 | password | testGRDB.swift:155:65:155:74 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:155:66:155:66 | password | password | +| testGRDB.swift:160:59:160:68 | [...] | testGRDB.swift:160:60:160:60 | password | testGRDB.swift:160:59:160:68 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:160:60:160:60 | password | password | +| testGRDB.swift:161:50:161:59 | [...] | testGRDB.swift:161:51:161:51 | password | testGRDB.swift:161:50:161:59 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:161:51:161:51 | password | password | +| testGRDB.swift:164:59:164:68 | [...] | testGRDB.swift:164:60:164:60 | password | testGRDB.swift:164:59:164:68 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:164:60:164:60 | password | password | +| testGRDB.swift:165:50:165:59 | [...] | testGRDB.swift:165:51:165:51 | password | testGRDB.swift:165:50:165:59 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:165:51:165:51 | password | password | +| testGRDB.swift:169:56:169:65 | [...] | testGRDB.swift:169:57:169:57 | password | testGRDB.swift:169:56:169:65 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:169:57:169:57 | password | password | +| testGRDB.swift:170:47:170:56 | [...] | testGRDB.swift:170:48:170:48 | password | testGRDB.swift:170:47:170:56 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:170:48:170:48 | password | password | +| testGRDB.swift:173:56:173:65 | [...] | testGRDB.swift:173:57:173:57 | password | testGRDB.swift:173:56:173:65 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:173:57:173:57 | password | password | +| testGRDB.swift:174:47:174:56 | [...] | testGRDB.swift:174:48:174:48 | password | testGRDB.swift:174:47:174:56 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:174:48:174:48 | password | password | +| testGRDB.swift:178:56:178:65 | [...] | testGRDB.swift:178:57:178:57 | password | testGRDB.swift:178:56:178:65 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:178:57:178:57 | password | password | +| testGRDB.swift:179:47:179:56 | [...] | testGRDB.swift:179:48:179:48 | password | testGRDB.swift:179:47:179:56 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:179:48:179:48 | password | password | +| testGRDB.swift:182:56:182:65 | [...] | testGRDB.swift:182:57:182:57 | password | testGRDB.swift:182:56:182:65 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:182:57:182:57 | password | password | +| testGRDB.swift:183:47:183:56 | [...] | testGRDB.swift:183:48:183:48 | password | testGRDB.swift:183:47:183:56 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:183:48:183:48 | password | password | +| testGRDB.swift:187:56:187:65 | [...] | testGRDB.swift:187:57:187:57 | password | testGRDB.swift:187:56:187:65 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:187:57:187:57 | password | password | +| testGRDB.swift:188:47:188:56 | [...] | testGRDB.swift:188:48:188:48 | password | testGRDB.swift:188:47:188:56 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:188:48:188:48 | password | password | +| testGRDB.swift:191:56:191:65 | [...] | testGRDB.swift:191:57:191:57 | password | testGRDB.swift:191:56:191:65 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:191:57:191:57 | password | password | +| testGRDB.swift:192:47:192:56 | [...] | testGRDB.swift:192:48:192:48 | password | testGRDB.swift:192:47:192:56 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:192:48:192:48 | password | password | +| testGRDB.swift:198:29:198:38 | [...] | testGRDB.swift:198:30:198:30 | password | testGRDB.swift:198:29:198:38 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:198:30:198:30 | password | password | +| testGRDB.swift:201:23:201:32 | [...] | testGRDB.swift:201:24:201:24 | password | testGRDB.swift:201:23:201:32 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:201:24:201:24 | password | password | +| testGRDB.swift:206:66:206:75 | [...] | testGRDB.swift:206:67:206:67 | password | testGRDB.swift:206:66:206:75 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:206:67:206:67 | password | password | +| testGRDB.swift:208:80:208:89 | [...] | testGRDB.swift:208:81:208:81 | password | testGRDB.swift:208:80:208:89 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:208:81:208:81 | password | password | +| testGRDB.swift:210:84:210:93 | [...] | testGRDB.swift:210:85:210:85 | password | testGRDB.swift:210:84:210:93 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:210:85:210:85 | password | password | +| testGRDB.swift:212:98:212:107 | [...] | testGRDB.swift:212:99:212:99 | password | testGRDB.swift:212:98:212:107 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:212:99:212:99 | password | password | +| testRealm2.swift:18:2:18:2 | o | testRealm2.swift:18:11:18:11 | myPassword | testRealm2.swift:18:2:18:2 | [post] o | This operation stores 'o' in a database. It may contain unencrypted sensitive data from $@. | testRealm2.swift:18:11:18:11 | myPassword | myPassword | +| testRealm2.swift:24:2:24:2 | o | testRealm2.swift:24:11:24:11 | socialSecurityNumber | testRealm2.swift:24:2:24:2 | [post] o | This operation stores 'o' in a database. It may contain unencrypted sensitive data from $@. | testRealm2.swift:24:11:24:11 | socialSecurityNumber | socialSecurityNumber | +| testRealm2.swift:25:2:25:2 | o | testRealm2.swift:25:11:25:11 | ssn | testRealm2.swift:25:2:25:2 | [post] o | This operation stores 'o' in a database. It may contain unencrypted sensitive data from $@. | testRealm2.swift:25:11:25:11 | ssn | ssn | +| testRealm2.swift:26:2:26:2 | o | testRealm2.swift:26:18:26:18 | ssn_int | testRealm2.swift:26:2:26:2 | [post] o | This operation stores 'o' in a database. It may contain unencrypted sensitive data from $@. | testRealm2.swift:26:18:26:18 | ssn_int | ssn_int | +| testRealm2.swift:32:2:32:2 | o | testRealm2.swift:32:11:32:11 | creditCardNumber | testRealm2.swift:32:2:32:2 | [post] o | This operation stores 'o' in a database. It may contain unencrypted sensitive data from $@. | testRealm2.swift:32:11:32:11 | creditCardNumber | creditCardNumber | +| testRealm2.swift:33:2:33:2 | o | testRealm2.swift:33:11:33:11 | CCN | testRealm2.swift:33:2:33:2 | [post] o | This operation stores 'o' in a database. It may contain unencrypted sensitive data from $@. | testRealm2.swift:33:11:33:11 | CCN | CCN | +| testRealm2.swift:34:2:34:2 | o | testRealm2.swift:34:18:34:18 | int_ccn | testRealm2.swift:34:2:34:2 | [post] o | This operation stores 'o' in a database. It may contain unencrypted sensitive data from $@. | testRealm2.swift:34:18:34:18 | int_ccn | int_ccn | +| testRealm.swift:41:2:41:2 | a | testRealm.swift:41:11:41:11 | myPassword | testRealm.swift:41:2:41:2 | [post] a | This operation stores 'a' in a database. It may contain unencrypted sensitive data from $@. | testRealm.swift:41:11:41:11 | myPassword | myPassword | +| testRealm.swift:49:2:49:2 | c | testRealm.swift:49:11:49:11 | myPassword | testRealm.swift:49:2:49:2 | [post] c | This operation stores 'c' in a database. It may contain unencrypted sensitive data from $@. | testRealm.swift:49:11:49:11 | myPassword | myPassword | +| testRealm.swift:59:2:59:3 | ...! | testRealm.swift:59:12:59:12 | myPassword | testRealm.swift:59:2:59:3 | [post] ...! | This operation stores '...!' in a database. It may contain unencrypted sensitive data from $@. | testRealm.swift:59:12:59:12 | myPassword | myPassword | +| testRealm.swift:66:2:66:2 | g | testRealm.swift:66:11:66:11 | myPassword | testRealm.swift:66:2:66:2 | [post] g | This operation stores 'g' in a database. It may contain unencrypted sensitive data from $@. | testRealm.swift:66:11:66:11 | myPassword | myPassword | +| testRealm.swift:73:2:73:2 | h | testRealm.swift:73:15:73:15 | myPassword | testRealm.swift:73:2:73:2 | [post] h | This operation stores 'h' in a database. It may contain unencrypted sensitive data from $@. | testRealm.swift:73:15:73:15 | myPassword | myPassword | edges | SQLite.swift:119:70:119:70 | mobilePhoneNumber | SQLite.swift:123:17:123:17 | insertQuery | provenance | | | SQLite.swift:119:70:119:70 | mobilePhoneNumber | SQLite.swift:127:21:127:21 | insertQuery | provenance | | @@ -622,143 +762,3 @@ subpaths | testRealm.swift:59:12:59:12 | myPassword | testRealm.swift:27:6:27:6 | value | testRealm.swift:27:6:27:6 | self [Return] [data] | testRealm.swift:59:2:59:3 | [post] ...! | | testRealm.swift:66:11:66:11 | myPassword | testRealm.swift:27:6:27:6 | value | testRealm.swift:27:6:27:6 | self [Return] [data] | testRealm.swift:66:2:66:2 | [post] g | | testRealm.swift:73:15:73:15 | myPassword | testRealm.swift:34:6:34:6 | value | testRealm.swift:34:6:34:6 | self [Return] [password] | testRealm.swift:73:2:73:2 | [post] h | -#select -| SQLite.swift:123:17:123:17 | insertQuery | SQLite.swift:119:70:119:70 | mobilePhoneNumber | SQLite.swift:123:17:123:17 | insertQuery | This operation stores 'insertQuery' in a database. It may contain unencrypted sensitive data from $@. | SQLite.swift:119:70:119:70 | mobilePhoneNumber | mobilePhoneNumber | -| SQLite.swift:124:17:124:17 | updateQuery | SQLite.swift:120:50:120:50 | mobilePhoneNumber | SQLite.swift:124:17:124:17 | updateQuery | This operation stores 'updateQuery' in a database. It may contain unencrypted sensitive data from $@. | SQLite.swift:120:50:120:50 | mobilePhoneNumber | mobilePhoneNumber | -| SQLite.swift:127:21:127:21 | insertQuery | SQLite.swift:119:70:119:70 | mobilePhoneNumber | SQLite.swift:127:21:127:21 | insertQuery | This operation stores 'insertQuery' in a database. It may contain unencrypted sensitive data from $@. | SQLite.swift:119:70:119:70 | mobilePhoneNumber | mobilePhoneNumber | -| SQLite.swift:128:21:128:21 | updateQuery | SQLite.swift:120:50:120:50 | mobilePhoneNumber | SQLite.swift:128:21:128:21 | updateQuery | This operation stores 'updateQuery' in a database. It may contain unencrypted sensitive data from $@. | SQLite.swift:120:50:120:50 | mobilePhoneNumber | mobilePhoneNumber | -| SQLite.swift:131:17:131:17 | insertQuery | SQLite.swift:119:70:119:70 | mobilePhoneNumber | SQLite.swift:131:17:131:17 | insertQuery | This operation stores 'insertQuery' in a database. It may contain unencrypted sensitive data from $@. | SQLite.swift:119:70:119:70 | mobilePhoneNumber | mobilePhoneNumber | -| SQLite.swift:132:17:132:17 | updateQuery | SQLite.swift:120:50:120:50 | mobilePhoneNumber | SQLite.swift:132:17:132:17 | updateQuery | This operation stores 'updateQuery' in a database. It may contain unencrypted sensitive data from $@. | SQLite.swift:120:50:120:50 | mobilePhoneNumber | mobilePhoneNumber | -| SQLite.swift:135:20:135:20 | insertQuery | SQLite.swift:119:70:119:70 | mobilePhoneNumber | SQLite.swift:135:20:135:20 | insertQuery | This operation stores 'insertQuery' in a database. It may contain unencrypted sensitive data from $@. | SQLite.swift:119:70:119:70 | mobilePhoneNumber | mobilePhoneNumber | -| SQLite.swift:136:20:136:20 | updateQuery | SQLite.swift:120:50:120:50 | mobilePhoneNumber | SQLite.swift:136:20:136:20 | updateQuery | This operation stores 'updateQuery' in a database. It may contain unencrypted sensitive data from $@. | SQLite.swift:120:50:120:50 | mobilePhoneNumber | mobilePhoneNumber | -| SQLite.swift:139:24:139:24 | insertQuery | SQLite.swift:119:70:119:70 | mobilePhoneNumber | SQLite.swift:139:24:139:24 | insertQuery | This operation stores 'insertQuery' in a database. It may contain unencrypted sensitive data from $@. | SQLite.swift:119:70:119:70 | mobilePhoneNumber | mobilePhoneNumber | -| SQLite.swift:140:24:140:24 | updateQuery | SQLite.swift:120:50:120:50 | mobilePhoneNumber | SQLite.swift:140:24:140:24 | updateQuery | This operation stores 'updateQuery' in a database. It may contain unencrypted sensitive data from $@. | SQLite.swift:120:50:120:50 | mobilePhoneNumber | mobilePhoneNumber | -| SQLite.swift:147:32:147:32 | [...] | SQLite.swift:147:32:147:32 | mobilePhoneNumber | SQLite.swift:147:32:147:32 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | SQLite.swift:147:32:147:32 | mobilePhoneNumber | mobilePhoneNumber | -| SQLite.swift:148:28:148:28 | [...] | SQLite.swift:148:28:148:28 | mobilePhoneNumber | SQLite.swift:148:28:148:28 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | SQLite.swift:148:28:148:28 | mobilePhoneNumber | mobilePhoneNumber | -| SQLite.swift:149:31:149:31 | [...] | SQLite.swift:149:31:149:31 | mobilePhoneNumber | SQLite.swift:149:31:149:31 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | SQLite.swift:149:31:149:31 | mobilePhoneNumber | mobilePhoneNumber | -| SQLite.swift:152:21:152:21 | [...] | SQLite.swift:152:21:152:21 | mobilePhoneNumber | SQLite.swift:152:21:152:21 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | SQLite.swift:152:21:152:21 | mobilePhoneNumber | mobilePhoneNumber | -| SQLite.swift:153:20:153:20 | [...] | SQLite.swift:153:20:153:20 | mobilePhoneNumber | SQLite.swift:153:20:153:20 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | SQLite.swift:153:20:153:20 | mobilePhoneNumber | mobilePhoneNumber | -| SQLite.swift:154:23:154:23 | [...] | SQLite.swift:154:23:154:23 | mobilePhoneNumber | SQLite.swift:154:23:154:23 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | SQLite.swift:154:23:154:23 | mobilePhoneNumber | mobilePhoneNumber | -| SQLite.swift:158:32:158:54 | [...] | SQLite.swift:158:33:158:33 | mobilePhoneNumber | SQLite.swift:158:32:158:54 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | SQLite.swift:158:33:158:33 | mobilePhoneNumber | mobilePhoneNumber | -| SQLite.swift:159:28:159:50 | [...] | SQLite.swift:159:29:159:29 | mobilePhoneNumber | SQLite.swift:159:28:159:50 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | SQLite.swift:159:29:159:29 | mobilePhoneNumber | mobilePhoneNumber | -| SQLite.swift:160:31:160:53 | [...] | SQLite.swift:160:32:160:32 | mobilePhoneNumber | SQLite.swift:160:31:160:53 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | SQLite.swift:160:32:160:32 | mobilePhoneNumber | mobilePhoneNumber | -| SQLite.swift:163:21:163:43 | [...] | SQLite.swift:163:22:163:22 | mobilePhoneNumber | SQLite.swift:163:21:163:43 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | SQLite.swift:163:22:163:22 | mobilePhoneNumber | mobilePhoneNumber | -| SQLite.swift:164:20:164:42 | [...] | SQLite.swift:164:21:164:21 | mobilePhoneNumber | SQLite.swift:164:20:164:42 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | SQLite.swift:164:21:164:21 | mobilePhoneNumber | mobilePhoneNumber | -| SQLite.swift:165:23:165:45 | [...] | SQLite.swift:165:24:165:24 | mobilePhoneNumber | SQLite.swift:165:23:165:45 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | SQLite.swift:165:24:165:24 | mobilePhoneNumber | mobilePhoneNumber | -| SQLite.swift:169:32:169:70 | [...] | SQLite.swift:169:53:169:53 | mobilePhoneNumber | SQLite.swift:169:32:169:70 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | SQLite.swift:169:53:169:53 | mobilePhoneNumber | mobilePhoneNumber | -| SQLite.swift:170:28:170:66 | [...] | SQLite.swift:170:49:170:49 | mobilePhoneNumber | SQLite.swift:170:28:170:66 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | SQLite.swift:170:49:170:49 | mobilePhoneNumber | mobilePhoneNumber | -| SQLite.swift:171:31:171:69 | [...] | SQLite.swift:171:52:171:52 | mobilePhoneNumber | SQLite.swift:171:31:171:69 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | SQLite.swift:171:52:171:52 | mobilePhoneNumber | mobilePhoneNumber | -| SQLite.swift:174:21:174:59 | [...] | SQLite.swift:174:42:174:42 | mobilePhoneNumber | SQLite.swift:174:21:174:59 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | SQLite.swift:174:42:174:42 | mobilePhoneNumber | mobilePhoneNumber | -| SQLite.swift:175:20:175:58 | [...] | SQLite.swift:175:41:175:41 | mobilePhoneNumber | SQLite.swift:175:20:175:58 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | SQLite.swift:175:41:175:41 | mobilePhoneNumber | mobilePhoneNumber | -| SQLite.swift:176:23:176:61 | [...] | SQLite.swift:176:44:176:44 | mobilePhoneNumber | SQLite.swift:176:23:176:61 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | SQLite.swift:176:44:176:44 | mobilePhoneNumber | mobilePhoneNumber | -| SQLite.swift:186:40:186:54 | [...] | SQLite.swift:186:54:186:54 | mobilePhoneNumber | SQLite.swift:186:40:186:54 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | SQLite.swift:186:54:186:54 | mobilePhoneNumber | mobilePhoneNumber | -| SQLite.swift:189:26:189:40 | [...] | SQLite.swift:189:40:189:40 | mobilePhoneNumber | SQLite.swift:189:26:189:40 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | SQLite.swift:189:40:189:40 | mobilePhoneNumber | mobilePhoneNumber | -| SQLite.swift:191:27:191:41 | [...] | SQLite.swift:191:41:191:41 | mobilePhoneNumber | SQLite.swift:191:27:191:41 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | SQLite.swift:191:41:191:41 | mobilePhoneNumber | mobilePhoneNumber | -| SQLite.swift:193:26:193:89 | [...] | SQLite.swift:193:72:193:72 | mobilePhoneNumber | SQLite.swift:193:26:193:89 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | SQLite.swift:193:72:193:72 | mobilePhoneNumber | mobilePhoneNumber | -| SQLite.swift:199:30:199:30 | badMany | SQLite.swift:197:32:197:32 | mobilePhoneNumber | SQLite.swift:199:30:199:30 | badMany | This operation stores 'badMany' in a database. It may contain unencrypted sensitive data from $@. | SQLite.swift:197:32:197:32 | mobilePhoneNumber | mobilePhoneNumber | -| SQLite.swift:201:54:201:54 | badMany | SQLite.swift:197:32:197:32 | mobilePhoneNumber | SQLite.swift:201:54:201:54 | badMany | This operation stores 'badMany' in a database. It may contain unencrypted sensitive data from $@. | SQLite.swift:197:32:197:32 | mobilePhoneNumber | mobilePhoneNumber | -| sqlite3_c_api.swift:46:27:46:27 | insertQuery | sqlite3_c_api.swift:42:69:42:69 | medicalNotes | sqlite3_c_api.swift:46:27:46:27 | insertQuery | This operation stores 'insertQuery' in a database. It may contain unencrypted sensitive data from $@. | sqlite3_c_api.swift:42:69:42:69 | medicalNotes | medicalNotes | -| sqlite3_c_api.swift:47:27:47:27 | updateQuery | sqlite3_c_api.swift:43:49:43:49 | medicalNotes | sqlite3_c_api.swift:47:27:47:27 | updateQuery | This operation stores 'updateQuery' in a database. It may contain unencrypted sensitive data from $@. | sqlite3_c_api.swift:43:49:43:49 | medicalNotes | medicalNotes | -| sqlite3_c_api.swift:58:36:58:36 | medicalNotes | sqlite3_c_api.swift:58:36:58:36 | medicalNotes | sqlite3_c_api.swift:58:36:58:36 | medicalNotes | This operation stores 'medicalNotes' in a database. It may contain unencrypted sensitive data from $@. | sqlite3_c_api.swift:58:36:58:36 | medicalNotes | medicalNotes | -| testCoreData2.swift:37:2:37:2 | obj | testCoreData2.swift:37:16:37:16 | bankAccountNo | testCoreData2.swift:37:2:37:2 | [post] obj | This operation stores 'obj' in a database. It may contain unencrypted sensitive data from $@. | testCoreData2.swift:37:16:37:16 | bankAccountNo | bankAccountNo | -| testCoreData2.swift:39:2:39:2 | obj | testCoreData2.swift:39:28:39:28 | bankAccountNo | testCoreData2.swift:39:2:39:2 | [post] obj | This operation stores 'obj' in a database. It may contain unencrypted sensitive data from $@. | testCoreData2.swift:39:28:39:28 | bankAccountNo | bankAccountNo | -| testCoreData2.swift:41:2:41:2 | obj | testCoreData2.swift:41:29:41:29 | bankAccountNo | testCoreData2.swift:41:2:41:2 | [post] obj | This operation stores 'obj' in a database. It may contain unencrypted sensitive data from $@. | testCoreData2.swift:41:29:41:29 | bankAccountNo | bankAccountNo | -| testCoreData2.swift:43:2:43:2 | obj | testCoreData2.swift:43:35:43:35 | bankAccountNo | testCoreData2.swift:43:2:43:2 | [post] obj | This operation stores 'obj' in a database. It may contain unencrypted sensitive data from $@. | testCoreData2.swift:43:35:43:35 | bankAccountNo | bankAccountNo | -| testCoreData2.swift:46:2:46:10 | ...? | testCoreData2.swift:46:22:46:22 | bankAccountNo | testCoreData2.swift:46:2:46:10 | [post] ...? | This operation stores '...?' in a database. It may contain unencrypted sensitive data from $@. | testCoreData2.swift:46:22:46:22 | bankAccountNo | bankAccountNo | -| testCoreData2.swift:48:2:48:10 | ...? | testCoreData2.swift:48:34:48:34 | bankAccountNo | testCoreData2.swift:48:2:48:10 | [post] ...? | This operation stores '...?' in a database. It may contain unencrypted sensitive data from $@. | testCoreData2.swift:48:34:48:34 | bankAccountNo | bankAccountNo | -| testCoreData2.swift:50:2:50:10 | ...? | testCoreData2.swift:50:35:50:35 | bankAccountNo | testCoreData2.swift:50:2:50:10 | [post] ...? | This operation stores '...?' in a database. It may contain unencrypted sensitive data from $@. | testCoreData2.swift:50:35:50:35 | bankAccountNo | bankAccountNo | -| testCoreData2.swift:52:2:52:10 | ...? | testCoreData2.swift:52:41:52:41 | bankAccountNo | testCoreData2.swift:52:2:52:10 | [post] ...? | This operation stores '...?' in a database. It may contain unencrypted sensitive data from $@. | testCoreData2.swift:52:41:52:41 | bankAccountNo | bankAccountNo | -| testCoreData2.swift:57:3:57:3 | obj | testCoreData2.swift:57:29:57:29 | bankAccountNo | testCoreData2.swift:57:3:57:3 | [post] obj | This operation stores 'obj' in a database. It may contain unencrypted sensitive data from $@. | testCoreData2.swift:57:29:57:29 | bankAccountNo | bankAccountNo | -| testCoreData2.swift:60:4:60:4 | obj | testCoreData2.swift:60:30:60:30 | bankAccountNo | testCoreData2.swift:60:4:60:4 | [post] obj | This operation stores 'obj' in a database. It may contain unencrypted sensitive data from $@. | testCoreData2.swift:60:30:60:30 | bankAccountNo | bankAccountNo | -| testCoreData2.swift:62:4:62:4 | obj | testCoreData2.swift:62:30:62:30 | bankAccountNo | testCoreData2.swift:62:4:62:4 | [post] obj | This operation stores 'obj' in a database. It may contain unencrypted sensitive data from $@. | testCoreData2.swift:62:30:62:30 | bankAccountNo | bankAccountNo | -| testCoreData2.swift:65:3:65:3 | obj | testCoreData2.swift:65:29:65:29 | bankAccountNo | testCoreData2.swift:65:3:65:3 | [post] obj | This operation stores 'obj' in a database. It may contain unencrypted sensitive data from $@. | testCoreData2.swift:65:29:65:29 | bankAccountNo | bankAccountNo | -| testCoreData2.swift:79:2:79:2 | dbObj | testCoreData2.swift:79:18:79:28 | .bankAccountNo | testCoreData2.swift:79:2:79:2 | [post] dbObj | This operation stores 'dbObj' in a database. It may contain unencrypted sensitive data from $@. | testCoreData2.swift:79:18:79:28 | .bankAccountNo | .bankAccountNo | -| testCoreData2.swift:80:2:80:2 | dbObj | testCoreData2.swift:80:18:80:28 | .bankAccountNo2 | testCoreData2.swift:80:2:80:2 | [post] dbObj | This operation stores 'dbObj' in a database. It may contain unencrypted sensitive data from $@. | testCoreData2.swift:80:18:80:28 | .bankAccountNo2 | .bankAccountNo2 | -| testCoreData2.swift:82:2:82:2 | dbObj | testCoreData2.swift:82:18:82:18 | bankAccountNo | testCoreData2.swift:82:2:82:2 | [post] dbObj | This operation stores 'dbObj' in a database. It may contain unencrypted sensitive data from $@. | testCoreData2.swift:82:18:82:18 | bankAccountNo | bankAccountNo | -| testCoreData2.swift:83:2:83:2 | dbObj | testCoreData2.swift:83:18:83:18 | bankAccountNo | testCoreData2.swift:83:2:83:2 | [post] dbObj | This operation stores 'dbObj' in a database. It may contain unencrypted sensitive data from $@. | testCoreData2.swift:83:18:83:18 | bankAccountNo | bankAccountNo | -| testCoreData2.swift:84:2:84:2 | dbObj | testCoreData2.swift:84:18:84:18 | bankAccountNo2 | testCoreData2.swift:84:2:84:2 | [post] dbObj | This operation stores 'dbObj' in a database. It may contain unencrypted sensitive data from $@. | testCoreData2.swift:84:18:84:18 | bankAccountNo2 | bankAccountNo2 | -| testCoreData2.swift:85:2:85:2 | dbObj | testCoreData2.swift:85:18:85:18 | bankAccountNo2 | testCoreData2.swift:85:2:85:2 | [post] dbObj | This operation stores 'dbObj' in a database. It may contain unencrypted sensitive data from $@. | testCoreData2.swift:85:18:85:18 | bankAccountNo2 | bankAccountNo2 | -| testCoreData2.swift:87:2:87:10 | ...? | testCoreData2.swift:87:22:87:32 | .bankAccountNo | testCoreData2.swift:87:2:87:10 | [post] ...? | This operation stores '...?' in a database. It may contain unencrypted sensitive data from $@. | testCoreData2.swift:87:22:87:32 | .bankAccountNo | .bankAccountNo | -| testCoreData2.swift:88:2:88:10 | ...? | testCoreData2.swift:88:22:88:22 | bankAccountNo | testCoreData2.swift:88:2:88:10 | [post] ...? | This operation stores '...?' in a database. It may contain unencrypted sensitive data from $@. | testCoreData2.swift:88:22:88:22 | bankAccountNo | bankAccountNo | -| testCoreData2.swift:89:2:89:10 | ...? | testCoreData2.swift:89:22:89:22 | bankAccountNo2 | testCoreData2.swift:89:2:89:10 | [post] ...? | This operation stores '...?' in a database. It may contain unencrypted sensitive data from $@. | testCoreData2.swift:89:22:89:22 | bankAccountNo2 | bankAccountNo2 | -| testCoreData2.swift:93:2:93:2 | dbObj | testCoreData2.swift:91:10:91:10 | bankAccountNo | testCoreData2.swift:93:2:93:2 | [post] dbObj | This operation stores 'dbObj' in a database. It may contain unencrypted sensitive data from $@. | testCoreData2.swift:91:10:91:10 | bankAccountNo | bankAccountNo | -| testCoreData2.swift:98:2:98:2 | dbObj | testCoreData2.swift:95:10:95:10 | bankAccountNo | testCoreData2.swift:98:2:98:2 | [post] dbObj | This operation stores 'dbObj' in a database. It may contain unencrypted sensitive data from $@. | testCoreData2.swift:95:10:95:10 | bankAccountNo | bankAccountNo | -| testCoreData2.swift:104:2:104:2 | dbObj | testCoreData2.swift:101:10:101:10 | bankAccountNo | testCoreData2.swift:104:2:104:2 | [post] dbObj | This operation stores 'dbObj' in a database. It may contain unencrypted sensitive data from $@. | testCoreData2.swift:101:10:101:10 | bankAccountNo | bankAccountNo | -| testCoreData2.swift:105:2:105:2 | dbObj | testCoreData2.swift:101:10:101:10 | bankAccountNo | testCoreData2.swift:105:2:105:2 | [post] dbObj | This operation stores 'dbObj' in a database. It may contain unencrypted sensitive data from $@. | testCoreData2.swift:101:10:101:10 | bankAccountNo | bankAccountNo | -| testCoreData.swift:19:12:19:12 | value | testCoreData.swift:61:25:61:25 | password | testCoreData.swift:19:12:19:12 | value | This operation stores 'value' in a database. It may contain unencrypted sensitive data from $@. | testCoreData.swift:61:25:61:25 | password | password | -| testCoreData.swift:32:13:32:13 | newValue | testCoreData.swift:64:16:64:16 | password | testCoreData.swift:32:13:32:13 | newValue | This operation stores 'newValue' in a database. It may contain unencrypted sensitive data from $@. | testCoreData.swift:64:16:64:16 | password | password | -| testCoreData.swift:48:15:48:15 | password | testCoreData.swift:48:15:48:15 | password | testCoreData.swift:48:15:48:15 | password | This operation stores 'password' in a database. It may contain unencrypted sensitive data from $@. | testCoreData.swift:48:15:48:15 | password | password | -| testCoreData.swift:51:24:51:24 | password | testCoreData.swift:51:24:51:24 | password | testCoreData.swift:51:24:51:24 | password | This operation stores 'password' in a database. It may contain unencrypted sensitive data from $@. | testCoreData.swift:51:24:51:24 | password | password | -| testCoreData.swift:58:15:58:15 | password | testCoreData.swift:58:15:58:15 | password | testCoreData.swift:58:15:58:15 | password | This operation stores 'password' in a database. It may contain unencrypted sensitive data from $@. | testCoreData.swift:58:15:58:15 | password | password | -| testCoreData.swift:64:2:64:2 | obj | testCoreData.swift:64:16:64:16 | password | testCoreData.swift:64:2:64:2 | [post] obj | This operation stores 'obj' in a database. It may contain unencrypted sensitive data from $@. | testCoreData.swift:64:16:64:16 | password | password | -| testCoreData.swift:78:15:78:15 | x | testCoreData.swift:77:24:77:24 | x | testCoreData.swift:78:15:78:15 | x | This operation stores 'x' in a database. It may contain unencrypted sensitive data from $@. | testCoreData.swift:77:24:77:24 | x | x | -| testCoreData.swift:81:15:81:15 | y | testCoreData.swift:80:10:80:22 | call to getPassword() | testCoreData.swift:81:15:81:15 | y | This operation stores 'y' in a database. It may contain unencrypted sensitive data from $@. | testCoreData.swift:80:10:80:22 | call to getPassword() | call to getPassword() | -| testCoreData.swift:85:15:85:17 | .password | testCoreData.swift:85:15:85:17 | .password | testCoreData.swift:85:15:85:17 | .password | This operation stores '.password' in a database. It may contain unencrypted sensitive data from $@. | testCoreData.swift:85:15:85:17 | .password | .password | -| testCoreData.swift:95:15:95:15 | x | testCoreData.swift:91:10:91:10 | passwd | testCoreData.swift:95:15:95:15 | x | This operation stores 'x' in a database. It may contain unencrypted sensitive data from $@. | testCoreData.swift:91:10:91:10 | passwd | passwd | -| testCoreData.swift:96:15:96:15 | y | testCoreData.swift:92:10:92:10 | passwd | testCoreData.swift:96:15:96:15 | y | This operation stores 'y' in a database. It may contain unencrypted sensitive data from $@. | testCoreData.swift:92:10:92:10 | passwd | passwd | -| testCoreData.swift:97:15:97:15 | z | testCoreData.swift:93:10:93:10 | passwd | testCoreData.swift:97:15:97:15 | z | This operation stores 'z' in a database. It may contain unencrypted sensitive data from $@. | testCoreData.swift:93:10:93:10 | passwd | passwd | -| testCoreData.swift:128:15:128:33 | call to generateSecretKey() | testCoreData.swift:128:15:128:33 | call to generateSecretKey() | testCoreData.swift:128:15:128:33 | call to generateSecretKey() | This operation stores 'call to generateSecretKey()' in a database. It may contain unencrypted sensitive data from $@. | testCoreData.swift:128:15:128:33 | call to generateSecretKey() | call to generateSecretKey() | -| testCoreData.swift:129:15:129:30 | call to getCertificate() | testCoreData.swift:129:15:129:30 | call to getCertificate() | testCoreData.swift:129:15:129:30 | call to getCertificate() | This operation stores 'call to getCertificate()' in a database. It may contain unencrypted sensitive data from $@. | testCoreData.swift:129:15:129:30 | call to getCertificate() | call to getCertificate() | -| testGRDB.swift:73:56:73:65 | [...] | testGRDB.swift:73:57:73:57 | password | testGRDB.swift:73:56:73:65 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:73:57:73:57 | password | password | -| testGRDB.swift:76:42:76:51 | [...] | testGRDB.swift:76:43:76:43 | password | testGRDB.swift:76:42:76:51 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:76:43:76:43 | password | password | -| testGRDB.swift:81:44:81:53 | [...] | testGRDB.swift:81:45:81:45 | password | testGRDB.swift:81:44:81:53 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:81:45:81:45 | password | password | -| testGRDB.swift:83:44:83:53 | [...] | testGRDB.swift:83:45:83:45 | password | testGRDB.swift:83:44:83:53 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:83:45:83:45 | password | password | -| testGRDB.swift:85:44:85:53 | [...] | testGRDB.swift:85:45:85:45 | password | testGRDB.swift:85:44:85:53 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:85:45:85:45 | password | password | -| testGRDB.swift:87:44:87:53 | [...] | testGRDB.swift:87:45:87:45 | password | testGRDB.swift:87:44:87:53 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:87:45:87:45 | password | password | -| testGRDB.swift:92:37:92:46 | [...] | testGRDB.swift:92:38:92:38 | password | testGRDB.swift:92:37:92:46 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:92:38:92:38 | password | password | -| testGRDB.swift:95:36:95:45 | [...] | testGRDB.swift:95:37:95:37 | password | testGRDB.swift:95:36:95:45 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:95:37:95:37 | password | password | -| testGRDB.swift:100:72:100:81 | [...] | testGRDB.swift:100:73:100:73 | password | testGRDB.swift:100:72:100:81 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:100:73:100:73 | password | password | -| testGRDB.swift:101:72:101:81 | [...] | testGRDB.swift:101:73:101:73 | password | testGRDB.swift:101:72:101:81 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:101:73:101:73 | password | password | -| testGRDB.swift:107:52:107:61 | [...] | testGRDB.swift:107:53:107:53 | password | testGRDB.swift:107:52:107:61 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:107:53:107:53 | password | password | -| testGRDB.swift:109:52:109:61 | [...] | testGRDB.swift:109:53:109:53 | password | testGRDB.swift:109:52:109:61 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:109:53:109:53 | password | password | -| testGRDB.swift:111:51:111:60 | [...] | testGRDB.swift:111:52:111:52 | password | testGRDB.swift:111:51:111:60 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:111:52:111:52 | password | password | -| testGRDB.swift:116:47:116:56 | [...] | testGRDB.swift:116:48:116:48 | password | testGRDB.swift:116:47:116:56 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:116:48:116:48 | password | password | -| testGRDB.swift:118:47:118:56 | [...] | testGRDB.swift:118:48:118:48 | password | testGRDB.swift:118:47:118:56 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:118:48:118:48 | password | password | -| testGRDB.swift:121:44:121:53 | [...] | testGRDB.swift:121:45:121:45 | password | testGRDB.swift:121:44:121:53 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:121:45:121:45 | password | password | -| testGRDB.swift:123:44:123:53 | [...] | testGRDB.swift:123:45:123:45 | password | testGRDB.swift:123:44:123:53 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:123:45:123:45 | password | password | -| testGRDB.swift:126:44:126:53 | [...] | testGRDB.swift:126:45:126:45 | password | testGRDB.swift:126:44:126:53 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:126:45:126:45 | password | password | -| testGRDB.swift:128:44:128:53 | [...] | testGRDB.swift:128:45:128:45 | password | testGRDB.swift:128:44:128:53 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:128:45:128:45 | password | password | -| testGRDB.swift:131:44:131:53 | [...] | testGRDB.swift:131:45:131:45 | password | testGRDB.swift:131:44:131:53 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:131:45:131:45 | password | password | -| testGRDB.swift:133:44:133:53 | [...] | testGRDB.swift:133:45:133:45 | password | testGRDB.swift:133:44:133:53 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:133:45:133:45 | password | password | -| testGRDB.swift:138:68:138:77 | [...] | testGRDB.swift:138:69:138:69 | password | testGRDB.swift:138:68:138:77 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:138:69:138:69 | password | password | -| testGRDB.swift:140:68:140:77 | [...] | testGRDB.swift:140:69:140:69 | password | testGRDB.swift:140:68:140:77 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:140:69:140:69 | password | password | -| testGRDB.swift:143:65:143:74 | [...] | testGRDB.swift:143:66:143:66 | password | testGRDB.swift:143:65:143:74 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:143:66:143:66 | password | password | -| testGRDB.swift:145:65:145:74 | [...] | testGRDB.swift:145:66:145:66 | password | testGRDB.swift:145:65:145:74 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:145:66:145:66 | password | password | -| testGRDB.swift:148:65:148:74 | [...] | testGRDB.swift:148:66:148:66 | password | testGRDB.swift:148:65:148:74 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:148:66:148:66 | password | password | -| testGRDB.swift:150:65:150:74 | [...] | testGRDB.swift:150:66:150:66 | password | testGRDB.swift:150:65:150:74 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:150:66:150:66 | password | password | -| testGRDB.swift:153:65:153:74 | [...] | testGRDB.swift:153:66:153:66 | password | testGRDB.swift:153:65:153:74 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:153:66:153:66 | password | password | -| testGRDB.swift:155:65:155:74 | [...] | testGRDB.swift:155:66:155:66 | password | testGRDB.swift:155:65:155:74 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:155:66:155:66 | password | password | -| testGRDB.swift:160:59:160:68 | [...] | testGRDB.swift:160:60:160:60 | password | testGRDB.swift:160:59:160:68 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:160:60:160:60 | password | password | -| testGRDB.swift:161:50:161:59 | [...] | testGRDB.swift:161:51:161:51 | password | testGRDB.swift:161:50:161:59 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:161:51:161:51 | password | password | -| testGRDB.swift:164:59:164:68 | [...] | testGRDB.swift:164:60:164:60 | password | testGRDB.swift:164:59:164:68 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:164:60:164:60 | password | password | -| testGRDB.swift:165:50:165:59 | [...] | testGRDB.swift:165:51:165:51 | password | testGRDB.swift:165:50:165:59 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:165:51:165:51 | password | password | -| testGRDB.swift:169:56:169:65 | [...] | testGRDB.swift:169:57:169:57 | password | testGRDB.swift:169:56:169:65 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:169:57:169:57 | password | password | -| testGRDB.swift:170:47:170:56 | [...] | testGRDB.swift:170:48:170:48 | password | testGRDB.swift:170:47:170:56 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:170:48:170:48 | password | password | -| testGRDB.swift:173:56:173:65 | [...] | testGRDB.swift:173:57:173:57 | password | testGRDB.swift:173:56:173:65 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:173:57:173:57 | password | password | -| testGRDB.swift:174:47:174:56 | [...] | testGRDB.swift:174:48:174:48 | password | testGRDB.swift:174:47:174:56 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:174:48:174:48 | password | password | -| testGRDB.swift:178:56:178:65 | [...] | testGRDB.swift:178:57:178:57 | password | testGRDB.swift:178:56:178:65 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:178:57:178:57 | password | password | -| testGRDB.swift:179:47:179:56 | [...] | testGRDB.swift:179:48:179:48 | password | testGRDB.swift:179:47:179:56 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:179:48:179:48 | password | password | -| testGRDB.swift:182:56:182:65 | [...] | testGRDB.swift:182:57:182:57 | password | testGRDB.swift:182:56:182:65 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:182:57:182:57 | password | password | -| testGRDB.swift:183:47:183:56 | [...] | testGRDB.swift:183:48:183:48 | password | testGRDB.swift:183:47:183:56 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:183:48:183:48 | password | password | -| testGRDB.swift:187:56:187:65 | [...] | testGRDB.swift:187:57:187:57 | password | testGRDB.swift:187:56:187:65 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:187:57:187:57 | password | password | -| testGRDB.swift:188:47:188:56 | [...] | testGRDB.swift:188:48:188:48 | password | testGRDB.swift:188:47:188:56 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:188:48:188:48 | password | password | -| testGRDB.swift:191:56:191:65 | [...] | testGRDB.swift:191:57:191:57 | password | testGRDB.swift:191:56:191:65 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:191:57:191:57 | password | password | -| testGRDB.swift:192:47:192:56 | [...] | testGRDB.swift:192:48:192:48 | password | testGRDB.swift:192:47:192:56 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:192:48:192:48 | password | password | -| testGRDB.swift:198:29:198:38 | [...] | testGRDB.swift:198:30:198:30 | password | testGRDB.swift:198:29:198:38 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:198:30:198:30 | password | password | -| testGRDB.swift:201:23:201:32 | [...] | testGRDB.swift:201:24:201:24 | password | testGRDB.swift:201:23:201:32 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:201:24:201:24 | password | password | -| testGRDB.swift:206:66:206:75 | [...] | testGRDB.swift:206:67:206:67 | password | testGRDB.swift:206:66:206:75 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:206:67:206:67 | password | password | -| testGRDB.swift:208:80:208:89 | [...] | testGRDB.swift:208:81:208:81 | password | testGRDB.swift:208:80:208:89 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:208:81:208:81 | password | password | -| testGRDB.swift:210:84:210:93 | [...] | testGRDB.swift:210:85:210:85 | password | testGRDB.swift:210:84:210:93 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:210:85:210:85 | password | password | -| testGRDB.swift:212:98:212:107 | [...] | testGRDB.swift:212:99:212:99 | password | testGRDB.swift:212:98:212:107 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:212:99:212:99 | password | password | -| testRealm2.swift:18:2:18:2 | o | testRealm2.swift:18:11:18:11 | myPassword | testRealm2.swift:18:2:18:2 | [post] o | This operation stores 'o' in a database. It may contain unencrypted sensitive data from $@. | testRealm2.swift:18:11:18:11 | myPassword | myPassword | -| testRealm2.swift:24:2:24:2 | o | testRealm2.swift:24:11:24:11 | socialSecurityNumber | testRealm2.swift:24:2:24:2 | [post] o | This operation stores 'o' in a database. It may contain unencrypted sensitive data from $@. | testRealm2.swift:24:11:24:11 | socialSecurityNumber | socialSecurityNumber | -| testRealm2.swift:25:2:25:2 | o | testRealm2.swift:25:11:25:11 | ssn | testRealm2.swift:25:2:25:2 | [post] o | This operation stores 'o' in a database. It may contain unencrypted sensitive data from $@. | testRealm2.swift:25:11:25:11 | ssn | ssn | -| testRealm2.swift:26:2:26:2 | o | testRealm2.swift:26:18:26:18 | ssn_int | testRealm2.swift:26:2:26:2 | [post] o | This operation stores 'o' in a database. It may contain unencrypted sensitive data from $@. | testRealm2.swift:26:18:26:18 | ssn_int | ssn_int | -| testRealm2.swift:32:2:32:2 | o | testRealm2.swift:32:11:32:11 | creditCardNumber | testRealm2.swift:32:2:32:2 | [post] o | This operation stores 'o' in a database. It may contain unencrypted sensitive data from $@. | testRealm2.swift:32:11:32:11 | creditCardNumber | creditCardNumber | -| testRealm2.swift:33:2:33:2 | o | testRealm2.swift:33:11:33:11 | CCN | testRealm2.swift:33:2:33:2 | [post] o | This operation stores 'o' in a database. It may contain unencrypted sensitive data from $@. | testRealm2.swift:33:11:33:11 | CCN | CCN | -| testRealm2.swift:34:2:34:2 | o | testRealm2.swift:34:18:34:18 | int_ccn | testRealm2.swift:34:2:34:2 | [post] o | This operation stores 'o' in a database. It may contain unencrypted sensitive data from $@. | testRealm2.swift:34:18:34:18 | int_ccn | int_ccn | -| testRealm.swift:41:2:41:2 | a | testRealm.swift:41:11:41:11 | myPassword | testRealm.swift:41:2:41:2 | [post] a | This operation stores 'a' in a database. It may contain unencrypted sensitive data from $@. | testRealm.swift:41:11:41:11 | myPassword | myPassword | -| testRealm.swift:49:2:49:2 | c | testRealm.swift:49:11:49:11 | myPassword | testRealm.swift:49:2:49:2 | [post] c | This operation stores 'c' in a database. It may contain unencrypted sensitive data from $@. | testRealm.swift:49:11:49:11 | myPassword | myPassword | -| testRealm.swift:59:2:59:3 | ...! | testRealm.swift:59:12:59:12 | myPassword | testRealm.swift:59:2:59:3 | [post] ...! | This operation stores '...!' in a database. It may contain unencrypted sensitive data from $@. | testRealm.swift:59:12:59:12 | myPassword | myPassword | -| testRealm.swift:66:2:66:2 | g | testRealm.swift:66:11:66:11 | myPassword | testRealm.swift:66:2:66:2 | [post] g | This operation stores 'g' in a database. It may contain unencrypted sensitive data from $@. | testRealm.swift:66:11:66:11 | myPassword | myPassword | -| testRealm.swift:73:2:73:2 | h | testRealm.swift:73:15:73:15 | myPassword | testRealm.swift:73:2:73:2 | [post] h | This operation stores 'h' in a database. It may contain unencrypted sensitive data from $@. | testRealm.swift:73:15:73:15 | myPassword | myPassword | diff --git a/swift/ql/test/query-tests/Security/CWE-311/CleartextStorageDatabase.qlref b/swift/ql/test/query-tests/Security/CWE-311/CleartextStorageDatabase.qlref index d73f4fc4bc2..0d588f51e61 100644 --- a/swift/ql/test/query-tests/Security/CWE-311/CleartextStorageDatabase.qlref +++ b/swift/ql/test/query-tests/Security/CWE-311/CleartextStorageDatabase.qlref @@ -1 +1,2 @@ -queries/Security/CWE-311/CleartextStorageDatabase.ql \ No newline at end of file +query: queries/Security/CWE-311/CleartextStorageDatabase.ql +postprocess: utils/test/InlineExpectationsTestQuery.ql diff --git a/swift/ql/test/query-tests/Security/CWE-311/CleartextTransmission.qlref b/swift/ql/test/query-tests/Security/CWE-311/CleartextTransmission.qlref index f4c5a561e61..3b301c53e7f 100644 --- a/swift/ql/test/query-tests/Security/CWE-311/CleartextTransmission.qlref +++ b/swift/ql/test/query-tests/Security/CWE-311/CleartextTransmission.qlref @@ -1 +1,2 @@ -queries/Security/CWE-311/CleartextTransmission.ql \ No newline at end of file +query: queries/Security/CWE-311/CleartextTransmission.ql +postprocess: utils/test/InlineExpectationsTestQuery.ql diff --git a/swift/ql/test/query-tests/Security/CWE-311/SQLite.swift b/swift/ql/test/query-tests/Security/CWE-311/SQLite.swift index 6874683d873..4b2f0923784 100644 --- a/swift/ql/test/query-tests/Security/CWE-311/SQLite.swift +++ b/swift/ql/test/query-tests/Security/CWE-311/SQLite.swift @@ -116,64 +116,64 @@ func ==(lhs: Expression, rhs: V) -> Expression { return Expression String { return myString } func test1(passwd : String, encrypted_passwd : String, account_no : String, credit_card_no : String) { - _ = URL(string: "http://example.com/login?p=" + passwd); // BAD + _ = URL(string: "http://example.com/login?p=" + passwd); // $ Alert[swift/cleartext-transmission] _ = URL(string: "http://example.com/login?p=" + encrypted_passwd); // GOOD (not sensitive) - _ = URL(string: "http://example.com/login?ac=" + account_no); // BAD - _ = URL(string: "http://example.com/login?cc=" + credit_card_no); // BAD + _ = URL(string: "http://example.com/login?ac=" + account_no); // $ Alert[swift/cleartext-transmission] + _ = URL(string: "http://example.com/login?cc=" + credit_card_no); // $ Alert[swift/cleartext-transmission] let base = URL(string: "http://example.com/"); // GOOD (not sensitive) _ = URL(string: "abc", relativeTo: base); // GOOD (not sensitive) - let f = URL(string: passwd, relativeTo: base); // BAD + let f = URL(string: passwd, relativeTo: base); // $ Alert[swift/cleartext-transmission] _ = URL(string: "abc", relativeTo: f); // BAD (reported on line above) let e_mail = myString - _ = URL(string: "http://example.com/login?em=" + e_mail); // BAD + _ = URL(string: "http://example.com/login?em=" + e_mail); // $ Alert[swift/cleartext-transmission] let a_homeaddr_z = getMyString() - _ = URL(string: "http://example.com/login?home=" + a_homeaddr_z); // BAD + _ = URL(string: "http://example.com/login?home=" + a_homeaddr_z); // $ Alert[swift/cleartext-transmission] let resident_ID = getMyString() - _ = URL(string: "http://example.com/login?id=" + resident_ID); // BAD + _ = URL(string: "http://example.com/login?id=" + resident_ID); // $ Alert[swift/cleartext-transmission] } func get_private_key() -> String { return "" } @@ -66,13 +66,13 @@ func get_certain() -> String { return "" } func test2() { // more variants... - _ = URL(string: "http://example.com/login?key=" + get_private_key()); // BAD [NOT DETECTED] - _ = URL(string: "http://example.com/login?key=" + get_aes_key()); // BAD [NOT DETECTED] - _ = URL(string: "http://example.com/login?key=" + get_aws_key()); // BAD [NOT DETECTED] - _ = URL(string: "http://example.com/login?key=" + get_access_key()); // BAD [NOT DETECTED] - _ = URL(string: "http://example.com/login?key=" + get_secret_key()); // BAD + _ = URL(string: "http://example.com/login?key=" + get_private_key()); // $ MISSING: Alert[swift/cleartext-transmission] // BAD [NOT DETECTED] + _ = URL(string: "http://example.com/login?key=" + get_aes_key()); // $ MISSING: Alert[swift/cleartext-transmission] // BAD [NOT DETECTED] + _ = URL(string: "http://example.com/login?key=" + get_aws_key()); // $ MISSING: Alert[swift/cleartext-transmission] // BAD [NOT DETECTED] + _ = URL(string: "http://example.com/login?key=" + get_access_key()); // $ MISSING: Alert[swift/cleartext-transmission] // BAD [NOT DETECTED] + _ = URL(string: "http://example.com/login?key=" + get_secret_key()); // $ Alert[swift/cleartext-transmission] _ = URL(string: "http://example.com/login?key=" + get_key_press()); // GOOD (not sensitive) - _ = URL(string: "http://example.com/login?cert=" + get_cert_string()); // BAD + _ = URL(string: "http://example.com/login?cert=" + get_cert_string()); // $ Alert[swift/cleartext-transmission] _ = URL(string: "http://example.com/login?certain=" + get_certain()); // GOOD (not sensitive) } @@ -90,20 +90,20 @@ func test3() { let auth_token = get_string() let next_token = get_string() - _ = URL(string: "http://example.com/login?key=\(priv_key)"); // BAD [NOT DETECTED] - _ = URL(string: "http://example.com/login?key=\(private_key)"); // BAD [NOT DETECTED] + _ = URL(string: "http://example.com/login?key=\(priv_key)"); // $ MISSING: Alert[swift/cleartext-transmission] // BAD [NOT DETECTED] + _ = URL(string: "http://example.com/login?key=\(private_key)"); // $ MISSING: Alert[swift/cleartext-transmission] // BAD [NOT DETECTED] _ = URL(string: "http://example.com/login?key=\(pub_key)"); // GOOD (not sensitive) - _ = URL(string: "http://example.com/login?cert=\(certificate)"); // BAD - _ = URL(string: "http://example.com/login?tok=\(secure_token)"); // BAD [NOT DETECTED] - _ = URL(string: "http://example.com/login?tok=\(access_token)"); // BAD [NOT DETECTED] - _ = URL(string: "http://example.com/login?tok=\(auth_token)"); // BAD [NOT DETECTED] + _ = URL(string: "http://example.com/login?cert=\(certificate)"); // $ Alert[swift/cleartext-transmission] + _ = URL(string: "http://example.com/login?tok=\(secure_token)"); // $ MISSING: Alert[swift/cleartext-transmission] // BAD [NOT DETECTED] + _ = URL(string: "http://example.com/login?tok=\(access_token)"); // $ MISSING: Alert[swift/cleartext-transmission] // BAD [NOT DETECTED] + _ = URL(string: "http://example.com/login?tok=\(auth_token)"); // $ MISSING: Alert[swift/cleartext-transmission] // BAD [NOT DETECTED] _ = URL(string: "http://example.com/login?tok=\(next_token)"); // GOOD (not sensitive) } func test4(key: SecKey) { - if let data = SecKeyCopyExternalRepresentation(key, nil) as? Data { + if let data = SecKeyCopyExternalRepresentation(key, nil) as? Data { // $ Source[swift/cleartext-transmission] if let string = String(data: data, encoding: .utf8) { - _ = URL(string: "http://example.com/login?tok=\(string)"); // BAD + _ = URL(string: "http://example.com/login?tok=\(string)"); // $ Alert[swift/cleartext-transmission] } } } @@ -113,14 +113,14 @@ func test5() { let email = get_string() let secret_key = get_string() - _ = URL(string: "http://example.com/login?email=\(email)"); // BAD + _ = URL(string: "http://example.com/login?email=\(email)"); // $ Alert[swift/cleartext-transmission] _ = URL(string: "mailto:\(email)"); // GOOD (revealing your e-amil address in an e-mail is expected) - _ = URL(string: "mailto:info@example.com?subject=\(secret_key)"); // BAD [NOT DETECTED] + _ = URL(string: "mailto:info@example.com?subject=\(secret_key)"); // $ MISSING: Alert[swift/cleartext-transmission] // BAD [NOT DETECTED] _ = URL(string: "mailto:info@example.com?subject=foo&cc=\(email)"); // GOOD let phone_number = get_string() - _ = URL(string: "http://example.com/profile?tel=\(phone_number)"); // BAD + _ = URL(string: "http://example.com/profile?tel=\(phone_number)"); // $ Alert[swift/cleartext-transmission] _ = URL(string: "tel:\(phone_number)") // GOOD _ = URL(string: "telprompt:\(phone_number)") // GOOD _ = URL(string: "callto:\(phone_number)") // GOOD @@ -129,5 +129,5 @@ func test5() { let account_no = get_string() _ = URL(string: "file:///foo/bar/\(account_no).csv") // GOOD (local, so not transmitted) - _ = URL(string: "ftp://example.com/\(account_no).csv") // BAD + _ = URL(string: "ftp://example.com/\(account_no).csv") // $ Alert[swift/cleartext-transmission] } diff --git a/swift/ql/test/query-tests/Security/CWE-312/CleartextStoragePreferences.expected b/swift/ql/test/query-tests/Security/CWE-312/CleartextStoragePreferences.expected index c3ed50e498c..9c412f25cee 100644 --- a/swift/ql/test/query-tests/Security/CWE-312/CleartextStoragePreferences.expected +++ b/swift/ql/test/query-tests/Security/CWE-312/CleartextStoragePreferences.expected @@ -1,3 +1,19 @@ +#select +| testNSUbiquitousKeyValueStore.swift:28:12:28:12 | password | testNSUbiquitousKeyValueStore.swift:28:12:28:12 | password | testNSUbiquitousKeyValueStore.swift:28:12:28:12 | password | This operation stores 'password' in iCloud. It may contain unencrypted sensitive data from $@. | testNSUbiquitousKeyValueStore.swift:28:12:28:12 | password | password | +| testNSUbiquitousKeyValueStore.swift:42:40:42:40 | x | testNSUbiquitousKeyValueStore.swift:41:24:41:24 | x | testNSUbiquitousKeyValueStore.swift:42:40:42:40 | x | This operation stores 'x' in iCloud. It may contain unencrypted sensitive data from $@. | testNSUbiquitousKeyValueStore.swift:41:24:41:24 | x | x | +| testNSUbiquitousKeyValueStore.swift:45:40:45:40 | y | testNSUbiquitousKeyValueStore.swift:44:10:44:22 | call to getPassword() | testNSUbiquitousKeyValueStore.swift:45:40:45:40 | y | This operation stores 'y' in iCloud. It may contain unencrypted sensitive data from $@. | testNSUbiquitousKeyValueStore.swift:44:10:44:22 | call to getPassword() | call to getPassword() | +| testNSUbiquitousKeyValueStore.swift:49:40:49:42 | .password | testNSUbiquitousKeyValueStore.swift:49:40:49:42 | .password | testNSUbiquitousKeyValueStore.swift:49:40:49:42 | .password | This operation stores '.password' in iCloud. It may contain unencrypted sensitive data from $@. | testNSUbiquitousKeyValueStore.swift:49:40:49:42 | .password | .password | +| testNSUbiquitousKeyValueStore.swift:59:40:59:40 | x | testNSUbiquitousKeyValueStore.swift:55:10:55:10 | passwd | testNSUbiquitousKeyValueStore.swift:59:40:59:40 | x | This operation stores 'x' in iCloud. It may contain unencrypted sensitive data from $@. | testNSUbiquitousKeyValueStore.swift:55:10:55:10 | passwd | passwd | +| testNSUbiquitousKeyValueStore.swift:60:40:60:40 | y | testNSUbiquitousKeyValueStore.swift:56:10:56:10 | passwd | testNSUbiquitousKeyValueStore.swift:60:40:60:40 | y | This operation stores 'y' in iCloud. It may contain unencrypted sensitive data from $@. | testNSUbiquitousKeyValueStore.swift:56:10:56:10 | passwd | passwd | +| testNSUbiquitousKeyValueStore.swift:61:40:61:40 | z | testNSUbiquitousKeyValueStore.swift:57:10:57:10 | passwd | testNSUbiquitousKeyValueStore.swift:61:40:61:40 | z | This operation stores 'z' in iCloud. It may contain unencrypted sensitive data from $@. | testNSUbiquitousKeyValueStore.swift:57:10:57:10 | passwd | passwd | +| testUserDefaults.swift:28:15:28:15 | password | testUserDefaults.swift:28:15:28:15 | password | testUserDefaults.swift:28:15:28:15 | password | This operation stores 'password' in the user defaults database. It may contain unencrypted sensitive data from $@. | testUserDefaults.swift:28:15:28:15 | password | password | +| testUserDefaults.swift:42:28:42:28 | x | testUserDefaults.swift:41:24:41:24 | x | testUserDefaults.swift:42:28:42:28 | x | This operation stores 'x' in the user defaults database. It may contain unencrypted sensitive data from $@. | testUserDefaults.swift:41:24:41:24 | x | x | +| testUserDefaults.swift:45:28:45:28 | y | testUserDefaults.swift:44:10:44:22 | call to getPassword() | testUserDefaults.swift:45:28:45:28 | y | This operation stores 'y' in the user defaults database. It may contain unencrypted sensitive data from $@. | testUserDefaults.swift:44:10:44:22 | call to getPassword() | call to getPassword() | +| testUserDefaults.swift:49:28:49:30 | .password | testUserDefaults.swift:49:28:49:30 | .password | testUserDefaults.swift:49:28:49:30 | .password | This operation stores '.password' in the user defaults database. It may contain unencrypted sensitive data from $@. | testUserDefaults.swift:49:28:49:30 | .password | .password | +| testUserDefaults.swift:59:28:59:28 | x | testUserDefaults.swift:55:10:55:10 | passwd | testUserDefaults.swift:59:28:59:28 | x | This operation stores 'x' in the user defaults database. It may contain unencrypted sensitive data from $@. | testUserDefaults.swift:55:10:55:10 | passwd | passwd | +| testUserDefaults.swift:60:28:60:28 | y | testUserDefaults.swift:56:10:56:10 | passwd | testUserDefaults.swift:60:28:60:28 | y | This operation stores 'y' in the user defaults database. It may contain unencrypted sensitive data from $@. | testUserDefaults.swift:56:10:56:10 | passwd | passwd | +| testUserDefaults.swift:61:28:61:28 | z | testUserDefaults.swift:57:10:57:10 | passwd | testUserDefaults.swift:61:28:61:28 | z | This operation stores 'z' in the user defaults database. It may contain unencrypted sensitive data from $@. | testUserDefaults.swift:57:10:57:10 | passwd | passwd | +| testUserDefaults.swift:82:28:82:40 | .value | testUserDefaults.swift:82:28:82:31 | .password | testUserDefaults.swift:82:28:82:40 | .value | This operation stores '.value' in the user defaults database. It may contain unencrypted sensitive data from $@. | testUserDefaults.swift:82:28:82:31 | .password | .password | edges | file://:0:0:0:0 | self | file://:0:0:0:0 | .value | provenance | Config | | testNSUbiquitousKeyValueStore.swift:41:24:41:24 | x | testNSUbiquitousKeyValueStore.swift:42:40:42:40 | x | provenance | | @@ -45,19 +61,3 @@ nodes | testUserDefaults.swift:82:28:82:40 | .value | semmle.label | .value | subpaths | testUserDefaults.swift:82:28:82:31 | .password | testUserDefaults.swift:74:7:74:7 | self | file://:0:0:0:0 | .value | testUserDefaults.swift:82:28:82:40 | .value | -#select -| testNSUbiquitousKeyValueStore.swift:28:12:28:12 | password | testNSUbiquitousKeyValueStore.swift:28:12:28:12 | password | testNSUbiquitousKeyValueStore.swift:28:12:28:12 | password | This operation stores 'password' in iCloud. It may contain unencrypted sensitive data from $@. | testNSUbiquitousKeyValueStore.swift:28:12:28:12 | password | password | -| testNSUbiquitousKeyValueStore.swift:42:40:42:40 | x | testNSUbiquitousKeyValueStore.swift:41:24:41:24 | x | testNSUbiquitousKeyValueStore.swift:42:40:42:40 | x | This operation stores 'x' in iCloud. It may contain unencrypted sensitive data from $@. | testNSUbiquitousKeyValueStore.swift:41:24:41:24 | x | x | -| testNSUbiquitousKeyValueStore.swift:45:40:45:40 | y | testNSUbiquitousKeyValueStore.swift:44:10:44:22 | call to getPassword() | testNSUbiquitousKeyValueStore.swift:45:40:45:40 | y | This operation stores 'y' in iCloud. It may contain unencrypted sensitive data from $@. | testNSUbiquitousKeyValueStore.swift:44:10:44:22 | call to getPassword() | call to getPassword() | -| testNSUbiquitousKeyValueStore.swift:49:40:49:42 | .password | testNSUbiquitousKeyValueStore.swift:49:40:49:42 | .password | testNSUbiquitousKeyValueStore.swift:49:40:49:42 | .password | This operation stores '.password' in iCloud. It may contain unencrypted sensitive data from $@. | testNSUbiquitousKeyValueStore.swift:49:40:49:42 | .password | .password | -| testNSUbiquitousKeyValueStore.swift:59:40:59:40 | x | testNSUbiquitousKeyValueStore.swift:55:10:55:10 | passwd | testNSUbiquitousKeyValueStore.swift:59:40:59:40 | x | This operation stores 'x' in iCloud. It may contain unencrypted sensitive data from $@. | testNSUbiquitousKeyValueStore.swift:55:10:55:10 | passwd | passwd | -| testNSUbiquitousKeyValueStore.swift:60:40:60:40 | y | testNSUbiquitousKeyValueStore.swift:56:10:56:10 | passwd | testNSUbiquitousKeyValueStore.swift:60:40:60:40 | y | This operation stores 'y' in iCloud. It may contain unencrypted sensitive data from $@. | testNSUbiquitousKeyValueStore.swift:56:10:56:10 | passwd | passwd | -| testNSUbiquitousKeyValueStore.swift:61:40:61:40 | z | testNSUbiquitousKeyValueStore.swift:57:10:57:10 | passwd | testNSUbiquitousKeyValueStore.swift:61:40:61:40 | z | This operation stores 'z' in iCloud. It may contain unencrypted sensitive data from $@. | testNSUbiquitousKeyValueStore.swift:57:10:57:10 | passwd | passwd | -| testUserDefaults.swift:28:15:28:15 | password | testUserDefaults.swift:28:15:28:15 | password | testUserDefaults.swift:28:15:28:15 | password | This operation stores 'password' in the user defaults database. It may contain unencrypted sensitive data from $@. | testUserDefaults.swift:28:15:28:15 | password | password | -| testUserDefaults.swift:42:28:42:28 | x | testUserDefaults.swift:41:24:41:24 | x | testUserDefaults.swift:42:28:42:28 | x | This operation stores 'x' in the user defaults database. It may contain unencrypted sensitive data from $@. | testUserDefaults.swift:41:24:41:24 | x | x | -| testUserDefaults.swift:45:28:45:28 | y | testUserDefaults.swift:44:10:44:22 | call to getPassword() | testUserDefaults.swift:45:28:45:28 | y | This operation stores 'y' in the user defaults database. It may contain unencrypted sensitive data from $@. | testUserDefaults.swift:44:10:44:22 | call to getPassword() | call to getPassword() | -| testUserDefaults.swift:49:28:49:30 | .password | testUserDefaults.swift:49:28:49:30 | .password | testUserDefaults.swift:49:28:49:30 | .password | This operation stores '.password' in the user defaults database. It may contain unencrypted sensitive data from $@. | testUserDefaults.swift:49:28:49:30 | .password | .password | -| testUserDefaults.swift:59:28:59:28 | x | testUserDefaults.swift:55:10:55:10 | passwd | testUserDefaults.swift:59:28:59:28 | x | This operation stores 'x' in the user defaults database. It may contain unencrypted sensitive data from $@. | testUserDefaults.swift:55:10:55:10 | passwd | passwd | -| testUserDefaults.swift:60:28:60:28 | y | testUserDefaults.swift:56:10:56:10 | passwd | testUserDefaults.swift:60:28:60:28 | y | This operation stores 'y' in the user defaults database. It may contain unencrypted sensitive data from $@. | testUserDefaults.swift:56:10:56:10 | passwd | passwd | -| testUserDefaults.swift:61:28:61:28 | z | testUserDefaults.swift:57:10:57:10 | passwd | testUserDefaults.swift:61:28:61:28 | z | This operation stores 'z' in the user defaults database. It may contain unencrypted sensitive data from $@. | testUserDefaults.swift:57:10:57:10 | passwd | passwd | -| testUserDefaults.swift:82:28:82:40 | .value | testUserDefaults.swift:82:28:82:31 | .password | testUserDefaults.swift:82:28:82:40 | .value | This operation stores '.value' in the user defaults database. It may contain unencrypted sensitive data from $@. | testUserDefaults.swift:82:28:82:31 | .password | .password | diff --git a/swift/ql/test/query-tests/Security/CWE-312/CleartextStoragePreferences.qlref b/swift/ql/test/query-tests/Security/CWE-312/CleartextStoragePreferences.qlref index 574e0e17232..dfb639f1bea 100644 --- a/swift/ql/test/query-tests/Security/CWE-312/CleartextStoragePreferences.qlref +++ b/swift/ql/test/query-tests/Security/CWE-312/CleartextStoragePreferences.qlref @@ -1 +1,2 @@ -queries/Security/CWE-312/CleartextStoragePreferences.ql +query: queries/Security/CWE-312/CleartextStoragePreferences.ql +postprocess: utils/test/InlineExpectationsTestQuery.ql diff --git a/swift/ql/test/query-tests/Security/CWE-312/cleartextLoggingTest.swift b/swift/ql/test/query-tests/Security/CWE-312/cleartextLoggingTest.swift index 060d6c5041e..da4d5054304 100644 --- a/swift/ql/test/query-tests/Security/CWE-312/cleartextLoggingTest.swift +++ b/swift/ql/test/query-tests/Security/CWE-312/cleartextLoggingTest.swift @@ -164,24 +164,24 @@ class MyRemoteLogger { // --- tests --- func test1(password: String, passwordHash : String, passphrase: String, pass_phrase: String) { - print(password) // $ Alert - print(password, separator: "") // $ Alert - print("", separator: password) // $ Alert - print(password, separator: "", terminator: "") // $ Alert - print("", separator: password, terminator: "") // $ Alert - print("", separator: "", terminator: password) // $ Alert + print(password) // $ Alert[swift/cleartext-logging] + print(password, separator: "") // $ Alert[swift/cleartext-logging] + print("", separator: password) // $ Alert[swift/cleartext-logging] + print(password, separator: "", terminator: "") // $ Alert[swift/cleartext-logging] + print("", separator: password, terminator: "") // $ Alert[swift/cleartext-logging] + print("", separator: "", terminator: password) // $ Alert[swift/cleartext-logging] print(passwordHash) // safe - debugPrint(password) // $ Alert + debugPrint(password) // $ Alert[swift/cleartext-logging] - dump(password) // $ Alert + dump(password) // $ Alert[swift/cleartext-logging] - NSLog(password) // $ Alert - NSLog("%@", password) // $ Alert - NSLog("%@ %@", "", password) // $ Alert - NSLog("\(password)") // $ Alert - NSLogv("%@", getVaList([password])) // $ Alert - NSLogv("%@ %@", getVaList(["", password])) // $ Alert + NSLog(password) // $ Alert[swift/cleartext-logging] + NSLog("%@", password) // $ Alert[swift/cleartext-logging] + NSLog("%@ %@", "", password) // $ Alert[swift/cleartext-logging] + NSLog("\(password)") // $ Alert[swift/cleartext-logging] + NSLogv("%@", getVaList([password])) // $ Alert[swift/cleartext-logging] + NSLogv("%@ %@", getVaList(["", password])) // $ Alert[swift/cleartext-logging] NSLog(passwordHash) // safe NSLogv("%@", getVaList([passwordHash])) // safe @@ -217,12 +217,12 @@ func test1(password: String, passwordHash : String, passphrase: String, pass_phr log.fault("\(password, privacy: .public)") // $ MISSING: Alert log.fault("\(passwordHash, privacy: .public)") // safe - NSLog(passphrase) // $ Alert - NSLog(pass_phrase) // $ Alert + NSLog(passphrase) // $ Alert[swift/cleartext-logging] + NSLog(pass_phrase) // $ Alert[swift/cleartext-logging] os_log("%@", log: .default, type: .default, "") // safe - os_log("%@", log: .default, type: .default, password) // $ Alert - os_log("%@ %@ %@", log: .default, type: .default, "", "", password) // $ Alert + os_log("%@", log: .default, type: .default, password) // $ Alert[swift/cleartext-logging] + os_log("%@ %@ %@", log: .default, type: .default, "", "", password) // $ Alert[swift/cleartext-logging] } class MyClass { @@ -237,15 +237,15 @@ func test3(x: String) { // alternative evidence of sensitivity... NSLog(x) // $ MISSING: Alert - doSomething(password: x); // $ Source - NSLog(x) // $ Alert + doSomething(password: x); // $ Source[swift/cleartext-logging] + NSLog(x) // $ Alert[swift/cleartext-logging] - let y = getPassword(); // $ Source - NSLog(y) // $ Alert + let y = getPassword(); // $ Source[swift/cleartext-logging] + NSLog(y) // $ Alert[swift/cleartext-logging] let z = MyClass() NSLog(z.harmless) // safe - NSLog(z.password) // $ Alert + NSLog(z.password) // $ Alert[swift/cleartext-logging] } struct MyOuter { @@ -260,7 +260,7 @@ struct MyOuter { func test3(mo : MyOuter) { // struct members... - NSLog(mo.password.value) // $ Alert + NSLog(mo.password.value) // $ Alert[swift/cleartext-logging] NSLog(mo.harmless.value) // safe } @@ -283,40 +283,40 @@ func test4(harmless: String, password: String) { print(harmless, to: &myString1) print(myString1) // safe - print(password, to: &myString2) // $ Source - print(myString2) // $ Alert + print(password, to: &myString2) // $ Source[swift/cleartext-logging] + print(myString2) // $ Alert[swift/cleartext-logging] - print("log: " + password, to: &myString3) // $ Source - print(myString3) // $ Alert + print("log: " + password, to: &myString3) // $ Source[swift/cleartext-logging] + print(myString3) // $ Alert[swift/cleartext-logging] debugPrint(harmless, to: &myString4) debugPrint(myString4) // safe - debugPrint(password, to: &myString5) // $ Source - debugPrint(myString5) // $ Alert + debugPrint(password, to: &myString5) // $ Source[swift/cleartext-logging] + debugPrint(myString5) // $ Alert[swift/cleartext-logging] dump(harmless, to: &myString6) dump(myString6) // safe - dump(password, to: &myString7) // $ Source - dump(myString7) // $ Alert + dump(password, to: &myString7) // $ Source[swift/cleartext-logging] + dump(myString7) // $ Alert[swift/cleartext-logging] myString8.write(harmless) print(myString8) - myString9.write(password) // $ Source - print(myString9) // $ Alert + myString9.write(password) // $ Source[swift/cleartext-logging] + print(myString9) // $ Alert[swift/cleartext-logging] myString10.write(harmless) - myString10.write(password) // $ Source + myString10.write(password) // $ Source[swift/cleartext-logging] myString10.write(harmless) - print(myString10) // $ Alert + print(myString10) // $ Alert[swift/cleartext-logging] harmless.write(to: &myString11) print(myString11) - password.write(to: &myString12) // $ Source - print(myString12) // $ Alert + password.write(to: &myString12) // $ Source[swift/cleartext-logging] + print(myString12) // $ Alert[swift/cleartext-logging] print(password, to: &myString13) // $ safe - only printed to another string debugPrint(password, to: &myString13) // $ safe - only printed to another string @@ -331,59 +331,59 @@ func test5(password: String, caseNum: Int) { switch caseNum { case 0: - assert(false, password) // $ Alert + assert(false, password) // $ Alert[swift/cleartext-logging] case 1: - assertionFailure(password) // $ Alert + assertionFailure(password) // $ Alert[swift/cleartext-logging] case 2: - precondition(false, password) // $ Alert + precondition(false, password) // $ Alert[swift/cleartext-logging] case 3: - preconditionFailure(password) // $ Alert + preconditionFailure(password) // $ Alert[swift/cleartext-logging] default: - fatalError(password) // $ Alert + fatalError(password) // $ Alert[swift/cleartext-logging] } } func test6(passwordString: String) { - let e = NSException(name: NSExceptionName("exception"), reason: "\(passwordString) is incorrect!", userInfo: nil) // $ Alert + let e = NSException(name: NSExceptionName("exception"), reason: "\(passwordString) is incorrect!", userInfo: nil) // $ Alert[swift/cleartext-logging] e.raise() - NSException.raise(NSExceptionName("exception"), format: "\(passwordString) is incorrect!", arguments: getVaList([])) // $ Alert - NSException.raise(NSExceptionName("exception"), format: "%s is incorrect!", arguments: getVaList([passwordString])) // $ Alert + NSException.raise(NSExceptionName("exception"), format: "\(passwordString) is incorrect!", arguments: getVaList([])) // $ Alert[swift/cleartext-logging] + NSException.raise(NSExceptionName("exception"), format: "%s is incorrect!", arguments: getVaList([passwordString])) // $ Alert[swift/cleartext-logging] - _ = dprintf(0, "\(passwordString) is incorrect!") // $ Alert - _ = dprintf(0, "%s is incorrect!", passwordString) // $ Alert - _ = dprintf(0, "%s: %s is incorrect!", "foo", passwordString) // $ Alert - _ = vprintf("\(passwordString) is incorrect!", getVaList([])) // $ Alert - _ = vprintf("%s is incorrect!", getVaList([passwordString])) // $ Alert - _ = vfprintf(nil, "\(passwordString) is incorrect!", getVaList([])) // $ Alert - _ = vfprintf(nil, "%s is incorrect!", getVaList([passwordString])) // $ Alert + _ = dprintf(0, "\(passwordString) is incorrect!") // $ Alert[swift/cleartext-logging] + _ = dprintf(0, "%s is incorrect!", passwordString) // $ Alert[swift/cleartext-logging] + _ = dprintf(0, "%s: %s is incorrect!", "foo", passwordString) // $ Alert[swift/cleartext-logging] + _ = vprintf("\(passwordString) is incorrect!", getVaList([])) // $ Alert[swift/cleartext-logging] + _ = vprintf("%s is incorrect!", getVaList([passwordString])) // $ Alert[swift/cleartext-logging] + _ = vfprintf(nil, "\(passwordString) is incorrect!", getVaList([])) // $ Alert[swift/cleartext-logging] + _ = vfprintf(nil, "%s is incorrect!", getVaList([passwordString])) // $ Alert[swift/cleartext-logging] _ = vasprintf_l(nil, nil, "\(passwordString) is incorrect!", getVaList([])) // good (`sprintf` is not logging) _ = vasprintf_l(nil, nil, "%s is incorrect!", getVaList([passwordString])) // good (`sprintf` is not logging) } func test7(authKey: String, authKey2: Int, authKey3: Float, password: String, secret: String) { - log(message: authKey) // $ Alert - log(message: String(authKey2)) // $ Alert + log(message: authKey) // $ Alert[swift/cleartext-logging] + log(message: String(authKey2)) // $ Alert[swift/cleartext-logging] logging(message: authKey) // $ MISSING: Alert logfile(file: 0, message: authKey) // $ MISSING: Alert - logMessage(NSString(string: authKey)) // $ Alert - logInfo(authKey) // $ Alert - logError(errorMsg: authKey) // $ Alert + logMessage(NSString(string: authKey)) // $ Alert[swift/cleartext-logging] + logInfo(authKey) // $ Alert[swift/cleartext-logging] + logError(errorMsg: authKey) // $ Alert[swift/cleartext-logging] harmless(authKey) // GOOD: not logging _ = logarithm(authKey3) // GOOD: not logging doLogin(login: authKey) // GOOD: not logging let logger = LogFile() - let msg = "authKey: " + authKey // $ Source - logger.log(msg) // $ Alert - logger.trace(msg) // $ Alert - logger.debug(msg) // $ Alert - logger.info(NSString(string: msg)) // $ Alert - logger.notice(msg) // $ Alert - logger.warning(msg) // $ Alert - logger.error(msg) // $ Alert - logger.critical(msg) // $ Alert - logger.fatal(msg) // $ Alert + let msg = "authKey: " + authKey // $ Source[swift/cleartext-logging] + logger.log(msg) // $ Alert[swift/cleartext-logging] + logger.trace(msg) // $ Alert[swift/cleartext-logging] + logger.debug(msg) // $ Alert[swift/cleartext-logging] + logger.info(NSString(string: msg)) // $ Alert[swift/cleartext-logging] + logger.notice(msg) // $ Alert[swift/cleartext-logging] + logger.warning(msg) // $ Alert[swift/cleartext-logging] + logger.error(msg) // $ Alert[swift/cleartext-logging] + logger.critical(msg) // $ Alert[swift/cleartext-logging] + logger.fatal(msg) // $ Alert[swift/cleartext-logging] let logic = Logic() logic.addInt(authKey2) // GOOD: not logging diff --git a/swift/ql/test/query-tests/Security/CWE-312/testNSUbiquitousKeyValueStore.swift b/swift/ql/test/query-tests/Security/CWE-312/testNSUbiquitousKeyValueStore.swift index 20627a6483b..8715eaa3472 100644 --- a/swift/ql/test/query-tests/Security/CWE-312/testNSUbiquitousKeyValueStore.swift +++ b/swift/ql/test/query-tests/Security/CWE-312/testNSUbiquitousKeyValueStore.swift @@ -25,7 +25,7 @@ func doSomething(password: String) { } func test1(password: String, passwordHash : String) { let store = NSUbiquitousKeyValueStore.default - store.set(password, forKey: "myKey") // BAD + store.set(password, forKey: "myKey") // $ Alert[swift/cleartext-storage-preferences] store.set(passwordHash, forKey: "myKey") // GOOD (not sensitive) } @@ -38,27 +38,27 @@ func test3(x: String) { // alternative evidence of sensitivity... NSUbiquitousKeyValueStore.default.set(x, forKey: "myKey") // BAD [NOT REPORTED] - doSomething(password: x); - NSUbiquitousKeyValueStore.default.set(x, forKey: "myKey") // BAD + doSomething(password: x); // $ Source[swift/cleartext-storage-preferences] + NSUbiquitousKeyValueStore.default.set(x, forKey: "myKey") // $ Alert[swift/cleartext-storage-preferences] - let y = getPassword(); - NSUbiquitousKeyValueStore.default.set(y, forKey: "myKey") // BAD + let y = getPassword(); // $ Source[swift/cleartext-storage-preferences] + NSUbiquitousKeyValueStore.default.set(y, forKey: "myKey") // $ Alert[swift/cleartext-storage-preferences] let z = MyClass() NSUbiquitousKeyValueStore.default.set(z.harmless, forKey: "myKey") // GOOD (not sensitive) - NSUbiquitousKeyValueStore.default.set(z.password, forKey: "myKey") // BAD + NSUbiquitousKeyValueStore.default.set(z.password, forKey: "myKey") // $ Alert[swift/cleartext-storage-preferences] } func test4(passwd: String) { // sanitizers... - var x = passwd; - var y = passwd; - var z = passwd; + var x = passwd; // $ Source[swift/cleartext-storage-preferences] + var y = passwd; // $ Source[swift/cleartext-storage-preferences] + var z = passwd; // $ Source[swift/cleartext-storage-preferences] - NSUbiquitousKeyValueStore.default.set(x, forKey: "myKey") // BAD - NSUbiquitousKeyValueStore.default.set(y, forKey: "myKey") // BAD - NSUbiquitousKeyValueStore.default.set(z, forKey: "myKey") // BAD + NSUbiquitousKeyValueStore.default.set(x, forKey: "myKey") // $ Alert[swift/cleartext-storage-preferences] + NSUbiquitousKeyValueStore.default.set(y, forKey: "myKey") // $ Alert[swift/cleartext-storage-preferences] + NSUbiquitousKeyValueStore.default.set(z, forKey: "myKey") // $ Alert[swift/cleartext-storage-preferences] x = encrypt(x); hash(data: &y); diff --git a/swift/ql/test/query-tests/Security/CWE-312/testUserDefaults.swift b/swift/ql/test/query-tests/Security/CWE-312/testUserDefaults.swift index 10a1a04eedf..cae889e562d 100644 --- a/swift/ql/test/query-tests/Security/CWE-312/testUserDefaults.swift +++ b/swift/ql/test/query-tests/Security/CWE-312/testUserDefaults.swift @@ -25,7 +25,7 @@ func doSomething(password: String) { } func test1(password: String, passwordHash : String) { let defaults = UserDefaults.standard - defaults.set(password, forKey: "myKey") // BAD + defaults.set(password, forKey: "myKey") // $ Alert[swift/cleartext-storage-preferences] defaults.set(passwordHash, forKey: "myKey") // GOOD (not sensitive) } @@ -38,27 +38,27 @@ func test3(x: String) { // alternative evidence of sensitivity... UserDefaults.standard.set(x, forKey: "myKey") // BAD [NOT REPORTED] - doSomething(password: x); - UserDefaults.standard.set(x, forKey: "myKey") // BAD + doSomething(password: x); // $ Source[swift/cleartext-storage-preferences] + UserDefaults.standard.set(x, forKey: "myKey") // $ Alert[swift/cleartext-storage-preferences] - let y = getPassword(); - UserDefaults.standard.set(y, forKey: "myKey") // BAD + let y = getPassword(); // $ Source[swift/cleartext-storage-preferences] + UserDefaults.standard.set(y, forKey: "myKey") // $ Alert[swift/cleartext-storage-preferences] let z = MyClass() UserDefaults.standard.set(z.harmless, forKey: "myKey") // GOOD (not sensitive) - UserDefaults.standard.set(z.password, forKey: "myKey") // BAD + UserDefaults.standard.set(z.password, forKey: "myKey") // $ Alert[swift/cleartext-storage-preferences] } func test4(passwd: String) { // sanitizers... - var x = passwd; - var y = passwd; - var z = passwd; + var x = passwd; // $ Source[swift/cleartext-storage-preferences] + var y = passwd; // $ Source[swift/cleartext-storage-preferences] + var z = passwd; // $ Source[swift/cleartext-storage-preferences] - UserDefaults.standard.set(x, forKey: "myKey") // BAD - UserDefaults.standard.set(y, forKey: "myKey") // BAD - UserDefaults.standard.set(z, forKey: "myKey") // BAD + UserDefaults.standard.set(x, forKey: "myKey") // $ Alert[swift/cleartext-storage-preferences] + UserDefaults.standard.set(y, forKey: "myKey") // $ Alert[swift/cleartext-storage-preferences] + UserDefaults.standard.set(z, forKey: "myKey") // $ Alert[swift/cleartext-storage-preferences] x = encrypt(x); hash(data: &y); @@ -79,6 +79,6 @@ struct MyOuter { } func test5(mo : MyOuter) { - UserDefaults.standard.set(mo.password.value, forKey: "myKey") // BAD + UserDefaults.standard.set(mo.password.value, forKey: "myKey") // $ Alert[swift/cleartext-storage-preferences] UserDefaults.standard.set(mo.harmless.value, forKey: "myKey") // GOOD } diff --git a/swift/ql/test/query-tests/Security/CWE-327/ECBEncryption.qlref b/swift/ql/test/query-tests/Security/CWE-327/ECBEncryption.qlref index ac56a6338b0..bee507b1cd0 100644 --- a/swift/ql/test/query-tests/Security/CWE-327/ECBEncryption.qlref +++ b/swift/ql/test/query-tests/Security/CWE-327/ECBEncryption.qlref @@ -1 +1,2 @@ -queries/Security/CWE-327/ECBEncryption.ql \ No newline at end of file +query: queries/Security/CWE-327/ECBEncryption.ql +postprocess: utils/test/InlineExpectationsTestQuery.ql diff --git a/swift/ql/test/query-tests/Security/CWE-327/test.swift b/swift/ql/test/query-tests/Security/CWE-327/test.swift index 38226990561..2eb39595b93 100644 --- a/swift/ql/test/query-tests/Security/CWE-327/test.swift +++ b/swift/ql/test/query-tests/Security/CWE-327/test.swift @@ -36,7 +36,7 @@ func getRandomArray() -> Array { } func getECBBlockMode() -> BlockMode { - return ECB() + return ECB() // $ Source } func getCBCBlockMode() -> BlockMode { @@ -47,18 +47,18 @@ func getCBCBlockMode() -> BlockMode { func test1() { let key: Array = [0x2a, 0x3a, 0x80, 0x05, 0xaf, 0x46, 0x58, 0x2d, 0x66, 0x52, 0x10, 0xae, 0x86, 0xd3, 0x8e, 0x8f] - let ecb = ECB() + let ecb = ECB() // $ Source let iv = getRandomArray() let cbc = CBC(iv: iv) let padding = Padding.noPadding // AES test cases - let ab1 = AES(key: key, blockMode: ecb, padding: padding) // BAD - let ab2 = AES(key: key, blockMode: ecb) // BAD - let ab3 = AES(key: key, blockMode: ECB(), padding: padding) // BAD - let ab4 = AES(key: key, blockMode: ECB()) // BAD - let ab5 = AES(key: key, blockMode: getECBBlockMode(), padding: padding) // BAD - let ab6 = AES(key: key, blockMode: getECBBlockMode()) // BAD + let ab1 = AES(key: key, blockMode: ecb, padding: padding) // $ Alert + let ab2 = AES(key: key, blockMode: ecb) // $ Alert + let ab3 = AES(key: key, blockMode: ECB(), padding: padding) // $ Alert + let ab4 = AES(key: key, blockMode: ECB()) // $ Alert + let ab5 = AES(key: key, blockMode: getECBBlockMode(), padding: padding) // $ Alert + let ab6 = AES(key: key, blockMode: getECBBlockMode()) // $ Alert let ag1 = AES(key: key, blockMode: cbc, padding: padding) // GOOD let ag2 = AES(key: key, blockMode: cbc) // GOOD @@ -68,9 +68,9 @@ func test1() { let ag6 = AES(key: key, blockMode: getCBCBlockMode()) // GOOD // Blowfish test cases - let bb1 = Blowfish(key: key, blockMode: ecb, padding: padding) // BAD - let bb2 = Blowfish(key: key, blockMode: ECB(), padding: padding) // BAD - let bb3 = Blowfish(key: key, blockMode: getECBBlockMode(), padding: padding) // BAD + let bb1 = Blowfish(key: key, blockMode: ecb, padding: padding) // $ Alert + let bb2 = Blowfish(key: key, blockMode: ECB(), padding: padding) // $ Alert + let bb3 = Blowfish(key: key, blockMode: getECBBlockMode(), padding: padding) // $ Alert let bg1 = Blowfish(key: key, blockMode: cbc, padding: padding) // GOOD let bg2 = Blowfish(key: key, blockMode: CBC(iv: iv), padding: padding) // GOOD diff --git a/swift/ql/test/query-tests/Security/CWE-328/WeakPasswordHashing.expected b/swift/ql/test/query-tests/Security/CWE-328/WeakPasswordHashing.expected index 273f26164fd..2b0eed8d0c2 100644 --- a/swift/ql/test/query-tests/Security/CWE-328/WeakPasswordHashing.expected +++ b/swift/ql/test/query-tests/Security/CWE-328/WeakPasswordHashing.expected @@ -1,3 +1,52 @@ +#select +| testCryptoKit.swift:84:47:84:47 | passwd | testCryptoKit.swift:84:47:84:47 | passwd | testCryptoKit.swift:84:47:84:47 | passwd | Insecure hashing algorithm (MD5) depends on $@. | testCryptoKit.swift:84:47:84:47 | passwd | password (passwd) | +| testCryptoKit.swift:85:52:85:52 | passwd | testCryptoKit.swift:85:52:85:52 | passwd | testCryptoKit.swift:85:52:85:52 | passwd | Insecure hashing algorithm (MD5) depends on $@. | testCryptoKit.swift:85:52:85:52 | passwd | password (passwd) | +| testCryptoKit.swift:91:36:91:36 | passwd | testCryptoKit.swift:91:36:91:36 | passwd | testCryptoKit.swift:91:36:91:36 | passwd | Insecure hashing algorithm (MD5) depends on $@. | testCryptoKit.swift:91:36:91:36 | passwd | password (passwd) | +| testCryptoKit.swift:92:45:92:45 | passwd | testCryptoKit.swift:92:45:92:45 | passwd | testCryptoKit.swift:92:45:92:45 | passwd | Insecure hashing algorithm (MD5) depends on $@. | testCryptoKit.swift:92:45:92:45 | passwd | password (passwd) | +| testCryptoKit.swift:98:44:98:44 | passwd | testCryptoKit.swift:98:44:98:44 | passwd | testCryptoKit.swift:98:44:98:44 | passwd | Insecure hashing algorithm (SHA1) depends on $@. | testCryptoKit.swift:98:44:98:44 | passwd | password (passwd) | +| testCryptoKit.swift:99:53:99:53 | passwd | testCryptoKit.swift:99:53:99:53 | passwd | testCryptoKit.swift:99:53:99:53 | passwd | Insecure hashing algorithm (SHA1) depends on $@. | testCryptoKit.swift:99:53:99:53 | passwd | password (passwd) | +| testCryptoKit.swift:105:37:105:37 | passwd | testCryptoKit.swift:105:37:105:37 | passwd | testCryptoKit.swift:105:37:105:37 | passwd | Insecure hashing algorithm (SHA256) depends on $@. | testCryptoKit.swift:105:37:105:37 | passwd | password (passwd) | +| testCryptoKit.swift:106:46:106:46 | passwd | testCryptoKit.swift:106:46:106:46 | passwd | testCryptoKit.swift:106:46:106:46 | passwd | Insecure hashing algorithm (SHA256) depends on $@. | testCryptoKit.swift:106:46:106:46 | passwd | password (passwd) | +| testCryptoKit.swift:112:37:112:37 | passwd | testCryptoKit.swift:112:37:112:37 | passwd | testCryptoKit.swift:112:37:112:37 | passwd | Insecure hashing algorithm (SHA384) depends on $@. | testCryptoKit.swift:112:37:112:37 | passwd | password (passwd) | +| testCryptoKit.swift:113:46:113:46 | passwd | testCryptoKit.swift:113:46:113:46 | passwd | testCryptoKit.swift:113:46:113:46 | passwd | Insecure hashing algorithm (SHA384) depends on $@. | testCryptoKit.swift:113:46:113:46 | passwd | password (passwd) | +| testCryptoKit.swift:119:37:119:37 | passwd | testCryptoKit.swift:119:37:119:37 | passwd | testCryptoKit.swift:119:37:119:37 | passwd | Insecure hashing algorithm (SHA512) depends on $@. | testCryptoKit.swift:119:37:119:37 | passwd | password (passwd) | +| testCryptoKit.swift:120:46:120:46 | passwd | testCryptoKit.swift:120:46:120:46 | passwd | testCryptoKit.swift:120:46:120:46 | passwd | Insecure hashing algorithm (SHA512) depends on $@. | testCryptoKit.swift:120:46:120:46 | passwd | password (passwd) | +| testCryptoKit.swift:129:23:129:23 | passwd | testCryptoKit.swift:129:23:129:23 | passwd | testCryptoKit.swift:129:23:129:23 | passwd | Insecure hashing algorithm (MD5) depends on $@. | testCryptoKit.swift:129:23:129:23 | passwd | password (passwd) | +| testCryptoKit.swift:138:23:138:23 | passwd | testCryptoKit.swift:138:23:138:23 | passwd | testCryptoKit.swift:138:23:138:23 | passwd | Insecure hashing algorithm (SHA1) depends on $@. | testCryptoKit.swift:138:23:138:23 | passwd | password (passwd) | +| testCryptoKit.swift:147:23:147:23 | passwd | testCryptoKit.swift:147:23:147:23 | passwd | testCryptoKit.swift:147:23:147:23 | passwd | Insecure hashing algorithm (SHA256) depends on $@. | testCryptoKit.swift:147:23:147:23 | passwd | password (passwd) | +| testCryptoKit.swift:156:23:156:23 | passwd | testCryptoKit.swift:156:23:156:23 | passwd | testCryptoKit.swift:156:23:156:23 | passwd | Insecure hashing algorithm (SHA384) depends on $@. | testCryptoKit.swift:156:23:156:23 | passwd | password (passwd) | +| testCryptoKit.swift:165:23:165:23 | passwd | testCryptoKit.swift:165:23:165:23 | passwd | testCryptoKit.swift:165:23:165:23 | passwd | Insecure hashing algorithm (SHA512) depends on $@. | testCryptoKit.swift:165:23:165:23 | passwd | password (passwd) | +| testCryptoKit.swift:174:32:174:32 | passwd | testCryptoKit.swift:174:32:174:32 | passwd | testCryptoKit.swift:174:32:174:32 | passwd | Insecure hashing algorithm (MD5) depends on $@. | testCryptoKit.swift:174:32:174:32 | passwd | password (passwd) | +| testCryptoKit.swift:183:32:183:32 | passwd | testCryptoKit.swift:183:32:183:32 | passwd | testCryptoKit.swift:183:32:183:32 | passwd | Insecure hashing algorithm (SHA1) depends on $@. | testCryptoKit.swift:183:32:183:32 | passwd | password (passwd) | +| testCryptoKit.swift:192:32:192:32 | passwd | testCryptoKit.swift:192:32:192:32 | passwd | testCryptoKit.swift:192:32:192:32 | passwd | Insecure hashing algorithm (SHA256) depends on $@. | testCryptoKit.swift:192:32:192:32 | passwd | password (passwd) | +| testCryptoKit.swift:201:32:201:32 | passwd | testCryptoKit.swift:201:32:201:32 | passwd | testCryptoKit.swift:201:32:201:32 | passwd | Insecure hashing algorithm (SHA384) depends on $@. | testCryptoKit.swift:201:32:201:32 | passwd | password (passwd) | +| testCryptoKit.swift:210:32:210:32 | passwd | testCryptoKit.swift:210:32:210:32 | passwd | testCryptoKit.swift:210:32:210:32 | passwd | Insecure hashing algorithm (SHA512) depends on $@. | testCryptoKit.swift:210:32:210:32 | passwd | password (passwd) | +| testCryptoKit.swift:220:49:220:49 | passwordData | testCryptoKit.swift:220:49:220:49 | passwordData | testCryptoKit.swift:220:49:220:49 | passwordData | Insecure hashing algorithm (SHA512) depends on $@. | testCryptoKit.swift:220:49:220:49 | passwordData | password (passwordData) | +| testCryptoKit.swift:224:33:224:57 | call to Data.init(_:) | testCryptoKit.swift:224:38:224:38 | passwordString | testCryptoKit.swift:224:33:224:57 | call to Data.init(_:) | Insecure hashing algorithm (SHA512) depends on $@. | testCryptoKit.swift:224:38:224:38 | passwordString | password (passwordString) | +| testCryptoSwift.swift:154:30:154:30 | passwdArray | testCryptoSwift.swift:154:30:154:30 | passwdArray | testCryptoSwift.swift:154:30:154:30 | passwdArray | Insecure hashing algorithm (MD5) depends on $@. | testCryptoSwift.swift:154:30:154:30 | passwdArray | password (passwdArray) | +| testCryptoSwift.swift:157:31:157:31 | passwdArray | testCryptoSwift.swift:157:31:157:31 | passwdArray | testCryptoSwift.swift:157:31:157:31 | passwdArray | Insecure hashing algorithm (SHA1) depends on $@. | testCryptoSwift.swift:157:31:157:31 | passwdArray | password (passwdArray) | +| testCryptoSwift.swift:160:47:160:47 | passwdArray | testCryptoSwift.swift:160:47:160:47 | passwdArray | testCryptoSwift.swift:160:47:160:47 | passwdArray | Insecure hashing algorithm (SHA2) depends on $@. | testCryptoSwift.swift:160:47:160:47 | passwdArray | password (passwdArray) | +| testCryptoSwift.swift:163:47:163:47 | passwdArray | testCryptoSwift.swift:163:47:163:47 | passwdArray | testCryptoSwift.swift:163:47:163:47 | passwdArray | Insecure hashing algorithm (SHA2) depends on $@. | testCryptoSwift.swift:163:47:163:47 | passwdArray | password (passwdArray) | +| testCryptoSwift.swift:167:20:167:20 | passwdArray | testCryptoSwift.swift:167:20:167:20 | passwdArray | testCryptoSwift.swift:167:20:167:20 | passwdArray | Insecure hashing algorithm (MD5) depends on $@. | testCryptoSwift.swift:167:20:167:20 | passwdArray | password (passwdArray) | +| testCryptoSwift.swift:170:21:170:21 | passwdArray | testCryptoSwift.swift:170:21:170:21 | passwdArray | testCryptoSwift.swift:170:21:170:21 | passwdArray | Insecure hashing algorithm (SHA1) depends on $@. | testCryptoSwift.swift:170:21:170:21 | passwdArray | password (passwdArray) | +| testCryptoSwift.swift:173:23:173:23 | passwdArray | testCryptoSwift.swift:173:23:173:23 | passwdArray | testCryptoSwift.swift:173:23:173:23 | passwdArray | Insecure hashing algorithm (SHA512) depends on $@. | testCryptoSwift.swift:173:23:173:23 | passwdArray | password (passwdArray) | +| testCryptoSwift.swift:176:21:176:21 | passwdArray | testCryptoSwift.swift:176:21:176:21 | passwdArray | testCryptoSwift.swift:176:21:176:21 | passwdArray | Insecure hashing algorithm (SHA2) depends on $@. | testCryptoSwift.swift:176:21:176:21 | passwdArray | password (passwdArray) | +| testCryptoSwift.swift:179:21:179:21 | passwdArray | testCryptoSwift.swift:179:21:179:21 | passwdArray | testCryptoSwift.swift:179:21:179:21 | passwdArray | Insecure hashing algorithm (SHA3) depends on $@. | testCryptoSwift.swift:179:21:179:21 | passwdArray | password (passwdArray) | +| testCryptoSwift.swift:183:9:183:9 | passwdArray | testCryptoSwift.swift:183:9:183:9 | passwdArray | testCryptoSwift.swift:183:9:183:9 | passwdArray | Insecure hashing algorithm (MD5) depends on $@. | testCryptoSwift.swift:183:9:183:9 | passwdArray | password (passwdArray) | +| testCryptoSwift.swift:186:9:186:9 | passwdArray | testCryptoSwift.swift:186:9:186:9 | passwdArray | testCryptoSwift.swift:186:9:186:9 | passwdArray | Insecure hashing algorithm (SHA1) depends on $@. | testCryptoSwift.swift:186:9:186:9 | passwdArray | password (passwdArray) | +| testCryptoSwift.swift:189:9:189:9 | passwdArray | testCryptoSwift.swift:189:9:189:9 | passwdArray | testCryptoSwift.swift:189:9:189:9 | passwdArray | Insecure hashing algorithm (SHA512) depends on $@. | testCryptoSwift.swift:189:9:189:9 | passwdArray | password (passwdArray) | +| testCryptoSwift.swift:192:9:192:9 | passwdArray | testCryptoSwift.swift:192:9:192:9 | passwdArray | testCryptoSwift.swift:192:9:192:9 | passwdArray | Insecure hashing algorithm (SHA2) depends on $@. | testCryptoSwift.swift:192:9:192:9 | passwdArray | password (passwdArray) | +| testCryptoSwift.swift:195:9:195:9 | passwdArray | testCryptoSwift.swift:195:9:195:9 | passwdArray | testCryptoSwift.swift:195:9:195:9 | passwdArray | Insecure hashing algorithm (SHA3) depends on $@. | testCryptoSwift.swift:195:9:195:9 | passwdArray | password (passwdArray) | +| testCryptoSwift.swift:201:9:201:9 | passwdData | testCryptoSwift.swift:201:9:201:9 | passwdData | testCryptoSwift.swift:201:9:201:9 | passwdData | Insecure hashing algorithm (MD5) depends on $@. | testCryptoSwift.swift:201:9:201:9 | passwdData | password (passwdData) | +| testCryptoSwift.swift:204:9:204:9 | passwdData | testCryptoSwift.swift:204:9:204:9 | passwdData | testCryptoSwift.swift:204:9:204:9 | passwdData | Insecure hashing algorithm (SHA1) depends on $@. | testCryptoSwift.swift:204:9:204:9 | passwdData | password (passwdData) | +| testCryptoSwift.swift:207:9:207:9 | passwdData | testCryptoSwift.swift:207:9:207:9 | passwdData | testCryptoSwift.swift:207:9:207:9 | passwdData | Insecure hashing algorithm (SHA512) depends on $@. | testCryptoSwift.swift:207:9:207:9 | passwdData | password (passwdData) | +| testCryptoSwift.swift:210:9:210:9 | passwdData | testCryptoSwift.swift:210:9:210:9 | passwdData | testCryptoSwift.swift:210:9:210:9 | passwdData | Insecure hashing algorithm (SHA2) depends on $@. | testCryptoSwift.swift:210:9:210:9 | passwdData | password (passwdData) | +| testCryptoSwift.swift:213:9:213:9 | passwdData | testCryptoSwift.swift:213:9:213:9 | passwdData | testCryptoSwift.swift:213:9:213:9 | passwdData | Insecure hashing algorithm (SHA3) depends on $@. | testCryptoSwift.swift:213:9:213:9 | passwdData | password (passwdData) | +| testCryptoSwift.swift:219:9:219:9 | passwd | testCryptoSwift.swift:219:9:219:9 | passwd | testCryptoSwift.swift:219:9:219:9 | passwd | Insecure hashing algorithm (MD5) depends on $@. | testCryptoSwift.swift:219:9:219:9 | passwd | password (passwd) | +| testCryptoSwift.swift:222:9:222:9 | passwd | testCryptoSwift.swift:222:9:222:9 | passwd | testCryptoSwift.swift:222:9:222:9 | passwd | Insecure hashing algorithm (SHA1) depends on $@. | testCryptoSwift.swift:222:9:222:9 | passwd | password (passwd) | +| testCryptoSwift.swift:225:9:225:9 | passwd | testCryptoSwift.swift:225:9:225:9 | passwd | testCryptoSwift.swift:225:9:225:9 | passwd | Insecure hashing algorithm (SHA512) depends on $@. | testCryptoSwift.swift:225:9:225:9 | passwd | password (passwd) | +| testCryptoSwift.swift:228:9:228:9 | passwd | testCryptoSwift.swift:228:9:228:9 | passwd | testCryptoSwift.swift:228:9:228:9 | passwd | Insecure hashing algorithm (SHA2) depends on $@. | testCryptoSwift.swift:228:9:228:9 | passwd | password (passwd) | +| testCryptoSwift.swift:231:9:231:9 | passwd | testCryptoSwift.swift:231:9:231:9 | passwd | testCryptoSwift.swift:231:9:231:9 | passwd | Insecure hashing algorithm (SHA3) depends on $@. | testCryptoSwift.swift:231:9:231:9 | passwd | password (passwd) | edges | testCryptoKit.swift:224:38:224:38 | passwordString | testCryptoKit.swift:224:38:224:53 | .utf8 | provenance | | | testCryptoKit.swift:224:38:224:53 | .utf8 | testCryptoKit.swift:224:33:224:57 | call to Data.init(_:) | provenance | | @@ -53,52 +102,3 @@ nodes | testCryptoSwift.swift:228:9:228:9 | passwd | semmle.label | passwd | | testCryptoSwift.swift:231:9:231:9 | passwd | semmle.label | passwd | subpaths -#select -| testCryptoKit.swift:84:47:84:47 | passwd | testCryptoKit.swift:84:47:84:47 | passwd | testCryptoKit.swift:84:47:84:47 | passwd | Insecure hashing algorithm (MD5) depends on $@. | testCryptoKit.swift:84:47:84:47 | passwd | password (passwd) | -| testCryptoKit.swift:85:52:85:52 | passwd | testCryptoKit.swift:85:52:85:52 | passwd | testCryptoKit.swift:85:52:85:52 | passwd | Insecure hashing algorithm (MD5) depends on $@. | testCryptoKit.swift:85:52:85:52 | passwd | password (passwd) | -| testCryptoKit.swift:91:36:91:36 | passwd | testCryptoKit.swift:91:36:91:36 | passwd | testCryptoKit.swift:91:36:91:36 | passwd | Insecure hashing algorithm (MD5) depends on $@. | testCryptoKit.swift:91:36:91:36 | passwd | password (passwd) | -| testCryptoKit.swift:92:45:92:45 | passwd | testCryptoKit.swift:92:45:92:45 | passwd | testCryptoKit.swift:92:45:92:45 | passwd | Insecure hashing algorithm (MD5) depends on $@. | testCryptoKit.swift:92:45:92:45 | passwd | password (passwd) | -| testCryptoKit.swift:98:44:98:44 | passwd | testCryptoKit.swift:98:44:98:44 | passwd | testCryptoKit.swift:98:44:98:44 | passwd | Insecure hashing algorithm (SHA1) depends on $@. | testCryptoKit.swift:98:44:98:44 | passwd | password (passwd) | -| testCryptoKit.swift:99:53:99:53 | passwd | testCryptoKit.swift:99:53:99:53 | passwd | testCryptoKit.swift:99:53:99:53 | passwd | Insecure hashing algorithm (SHA1) depends on $@. | testCryptoKit.swift:99:53:99:53 | passwd | password (passwd) | -| testCryptoKit.swift:105:37:105:37 | passwd | testCryptoKit.swift:105:37:105:37 | passwd | testCryptoKit.swift:105:37:105:37 | passwd | Insecure hashing algorithm (SHA256) depends on $@. | testCryptoKit.swift:105:37:105:37 | passwd | password (passwd) | -| testCryptoKit.swift:106:46:106:46 | passwd | testCryptoKit.swift:106:46:106:46 | passwd | testCryptoKit.swift:106:46:106:46 | passwd | Insecure hashing algorithm (SHA256) depends on $@. | testCryptoKit.swift:106:46:106:46 | passwd | password (passwd) | -| testCryptoKit.swift:112:37:112:37 | passwd | testCryptoKit.swift:112:37:112:37 | passwd | testCryptoKit.swift:112:37:112:37 | passwd | Insecure hashing algorithm (SHA384) depends on $@. | testCryptoKit.swift:112:37:112:37 | passwd | password (passwd) | -| testCryptoKit.swift:113:46:113:46 | passwd | testCryptoKit.swift:113:46:113:46 | passwd | testCryptoKit.swift:113:46:113:46 | passwd | Insecure hashing algorithm (SHA384) depends on $@. | testCryptoKit.swift:113:46:113:46 | passwd | password (passwd) | -| testCryptoKit.swift:119:37:119:37 | passwd | testCryptoKit.swift:119:37:119:37 | passwd | testCryptoKit.swift:119:37:119:37 | passwd | Insecure hashing algorithm (SHA512) depends on $@. | testCryptoKit.swift:119:37:119:37 | passwd | password (passwd) | -| testCryptoKit.swift:120:46:120:46 | passwd | testCryptoKit.swift:120:46:120:46 | passwd | testCryptoKit.swift:120:46:120:46 | passwd | Insecure hashing algorithm (SHA512) depends on $@. | testCryptoKit.swift:120:46:120:46 | passwd | password (passwd) | -| testCryptoKit.swift:129:23:129:23 | passwd | testCryptoKit.swift:129:23:129:23 | passwd | testCryptoKit.swift:129:23:129:23 | passwd | Insecure hashing algorithm (MD5) depends on $@. | testCryptoKit.swift:129:23:129:23 | passwd | password (passwd) | -| testCryptoKit.swift:138:23:138:23 | passwd | testCryptoKit.swift:138:23:138:23 | passwd | testCryptoKit.swift:138:23:138:23 | passwd | Insecure hashing algorithm (SHA1) depends on $@. | testCryptoKit.swift:138:23:138:23 | passwd | password (passwd) | -| testCryptoKit.swift:147:23:147:23 | passwd | testCryptoKit.swift:147:23:147:23 | passwd | testCryptoKit.swift:147:23:147:23 | passwd | Insecure hashing algorithm (SHA256) depends on $@. | testCryptoKit.swift:147:23:147:23 | passwd | password (passwd) | -| testCryptoKit.swift:156:23:156:23 | passwd | testCryptoKit.swift:156:23:156:23 | passwd | testCryptoKit.swift:156:23:156:23 | passwd | Insecure hashing algorithm (SHA384) depends on $@. | testCryptoKit.swift:156:23:156:23 | passwd | password (passwd) | -| testCryptoKit.swift:165:23:165:23 | passwd | testCryptoKit.swift:165:23:165:23 | passwd | testCryptoKit.swift:165:23:165:23 | passwd | Insecure hashing algorithm (SHA512) depends on $@. | testCryptoKit.swift:165:23:165:23 | passwd | password (passwd) | -| testCryptoKit.swift:174:32:174:32 | passwd | testCryptoKit.swift:174:32:174:32 | passwd | testCryptoKit.swift:174:32:174:32 | passwd | Insecure hashing algorithm (MD5) depends on $@. | testCryptoKit.swift:174:32:174:32 | passwd | password (passwd) | -| testCryptoKit.swift:183:32:183:32 | passwd | testCryptoKit.swift:183:32:183:32 | passwd | testCryptoKit.swift:183:32:183:32 | passwd | Insecure hashing algorithm (SHA1) depends on $@. | testCryptoKit.swift:183:32:183:32 | passwd | password (passwd) | -| testCryptoKit.swift:192:32:192:32 | passwd | testCryptoKit.swift:192:32:192:32 | passwd | testCryptoKit.swift:192:32:192:32 | passwd | Insecure hashing algorithm (SHA256) depends on $@. | testCryptoKit.swift:192:32:192:32 | passwd | password (passwd) | -| testCryptoKit.swift:201:32:201:32 | passwd | testCryptoKit.swift:201:32:201:32 | passwd | testCryptoKit.swift:201:32:201:32 | passwd | Insecure hashing algorithm (SHA384) depends on $@. | testCryptoKit.swift:201:32:201:32 | passwd | password (passwd) | -| testCryptoKit.swift:210:32:210:32 | passwd | testCryptoKit.swift:210:32:210:32 | passwd | testCryptoKit.swift:210:32:210:32 | passwd | Insecure hashing algorithm (SHA512) depends on $@. | testCryptoKit.swift:210:32:210:32 | passwd | password (passwd) | -| testCryptoKit.swift:220:49:220:49 | passwordData | testCryptoKit.swift:220:49:220:49 | passwordData | testCryptoKit.swift:220:49:220:49 | passwordData | Insecure hashing algorithm (SHA512) depends on $@. | testCryptoKit.swift:220:49:220:49 | passwordData | password (passwordData) | -| testCryptoKit.swift:224:33:224:57 | call to Data.init(_:) | testCryptoKit.swift:224:38:224:38 | passwordString | testCryptoKit.swift:224:33:224:57 | call to Data.init(_:) | Insecure hashing algorithm (SHA512) depends on $@. | testCryptoKit.swift:224:38:224:38 | passwordString | password (passwordString) | -| testCryptoSwift.swift:154:30:154:30 | passwdArray | testCryptoSwift.swift:154:30:154:30 | passwdArray | testCryptoSwift.swift:154:30:154:30 | passwdArray | Insecure hashing algorithm (MD5) depends on $@. | testCryptoSwift.swift:154:30:154:30 | passwdArray | password (passwdArray) | -| testCryptoSwift.swift:157:31:157:31 | passwdArray | testCryptoSwift.swift:157:31:157:31 | passwdArray | testCryptoSwift.swift:157:31:157:31 | passwdArray | Insecure hashing algorithm (SHA1) depends on $@. | testCryptoSwift.swift:157:31:157:31 | passwdArray | password (passwdArray) | -| testCryptoSwift.swift:160:47:160:47 | passwdArray | testCryptoSwift.swift:160:47:160:47 | passwdArray | testCryptoSwift.swift:160:47:160:47 | passwdArray | Insecure hashing algorithm (SHA2) depends on $@. | testCryptoSwift.swift:160:47:160:47 | passwdArray | password (passwdArray) | -| testCryptoSwift.swift:163:47:163:47 | passwdArray | testCryptoSwift.swift:163:47:163:47 | passwdArray | testCryptoSwift.swift:163:47:163:47 | passwdArray | Insecure hashing algorithm (SHA2) depends on $@. | testCryptoSwift.swift:163:47:163:47 | passwdArray | password (passwdArray) | -| testCryptoSwift.swift:167:20:167:20 | passwdArray | testCryptoSwift.swift:167:20:167:20 | passwdArray | testCryptoSwift.swift:167:20:167:20 | passwdArray | Insecure hashing algorithm (MD5) depends on $@. | testCryptoSwift.swift:167:20:167:20 | passwdArray | password (passwdArray) | -| testCryptoSwift.swift:170:21:170:21 | passwdArray | testCryptoSwift.swift:170:21:170:21 | passwdArray | testCryptoSwift.swift:170:21:170:21 | passwdArray | Insecure hashing algorithm (SHA1) depends on $@. | testCryptoSwift.swift:170:21:170:21 | passwdArray | password (passwdArray) | -| testCryptoSwift.swift:173:23:173:23 | passwdArray | testCryptoSwift.swift:173:23:173:23 | passwdArray | testCryptoSwift.swift:173:23:173:23 | passwdArray | Insecure hashing algorithm (SHA512) depends on $@. | testCryptoSwift.swift:173:23:173:23 | passwdArray | password (passwdArray) | -| testCryptoSwift.swift:176:21:176:21 | passwdArray | testCryptoSwift.swift:176:21:176:21 | passwdArray | testCryptoSwift.swift:176:21:176:21 | passwdArray | Insecure hashing algorithm (SHA2) depends on $@. | testCryptoSwift.swift:176:21:176:21 | passwdArray | password (passwdArray) | -| testCryptoSwift.swift:179:21:179:21 | passwdArray | testCryptoSwift.swift:179:21:179:21 | passwdArray | testCryptoSwift.swift:179:21:179:21 | passwdArray | Insecure hashing algorithm (SHA3) depends on $@. | testCryptoSwift.swift:179:21:179:21 | passwdArray | password (passwdArray) | -| testCryptoSwift.swift:183:9:183:9 | passwdArray | testCryptoSwift.swift:183:9:183:9 | passwdArray | testCryptoSwift.swift:183:9:183:9 | passwdArray | Insecure hashing algorithm (MD5) depends on $@. | testCryptoSwift.swift:183:9:183:9 | passwdArray | password (passwdArray) | -| testCryptoSwift.swift:186:9:186:9 | passwdArray | testCryptoSwift.swift:186:9:186:9 | passwdArray | testCryptoSwift.swift:186:9:186:9 | passwdArray | Insecure hashing algorithm (SHA1) depends on $@. | testCryptoSwift.swift:186:9:186:9 | passwdArray | password (passwdArray) | -| testCryptoSwift.swift:189:9:189:9 | passwdArray | testCryptoSwift.swift:189:9:189:9 | passwdArray | testCryptoSwift.swift:189:9:189:9 | passwdArray | Insecure hashing algorithm (SHA512) depends on $@. | testCryptoSwift.swift:189:9:189:9 | passwdArray | password (passwdArray) | -| testCryptoSwift.swift:192:9:192:9 | passwdArray | testCryptoSwift.swift:192:9:192:9 | passwdArray | testCryptoSwift.swift:192:9:192:9 | passwdArray | Insecure hashing algorithm (SHA2) depends on $@. | testCryptoSwift.swift:192:9:192:9 | passwdArray | password (passwdArray) | -| testCryptoSwift.swift:195:9:195:9 | passwdArray | testCryptoSwift.swift:195:9:195:9 | passwdArray | testCryptoSwift.swift:195:9:195:9 | passwdArray | Insecure hashing algorithm (SHA3) depends on $@. | testCryptoSwift.swift:195:9:195:9 | passwdArray | password (passwdArray) | -| testCryptoSwift.swift:201:9:201:9 | passwdData | testCryptoSwift.swift:201:9:201:9 | passwdData | testCryptoSwift.swift:201:9:201:9 | passwdData | Insecure hashing algorithm (MD5) depends on $@. | testCryptoSwift.swift:201:9:201:9 | passwdData | password (passwdData) | -| testCryptoSwift.swift:204:9:204:9 | passwdData | testCryptoSwift.swift:204:9:204:9 | passwdData | testCryptoSwift.swift:204:9:204:9 | passwdData | Insecure hashing algorithm (SHA1) depends on $@. | testCryptoSwift.swift:204:9:204:9 | passwdData | password (passwdData) | -| testCryptoSwift.swift:207:9:207:9 | passwdData | testCryptoSwift.swift:207:9:207:9 | passwdData | testCryptoSwift.swift:207:9:207:9 | passwdData | Insecure hashing algorithm (SHA512) depends on $@. | testCryptoSwift.swift:207:9:207:9 | passwdData | password (passwdData) | -| testCryptoSwift.swift:210:9:210:9 | passwdData | testCryptoSwift.swift:210:9:210:9 | passwdData | testCryptoSwift.swift:210:9:210:9 | passwdData | Insecure hashing algorithm (SHA2) depends on $@. | testCryptoSwift.swift:210:9:210:9 | passwdData | password (passwdData) | -| testCryptoSwift.swift:213:9:213:9 | passwdData | testCryptoSwift.swift:213:9:213:9 | passwdData | testCryptoSwift.swift:213:9:213:9 | passwdData | Insecure hashing algorithm (SHA3) depends on $@. | testCryptoSwift.swift:213:9:213:9 | passwdData | password (passwdData) | -| testCryptoSwift.swift:219:9:219:9 | passwd | testCryptoSwift.swift:219:9:219:9 | passwd | testCryptoSwift.swift:219:9:219:9 | passwd | Insecure hashing algorithm (MD5) depends on $@. | testCryptoSwift.swift:219:9:219:9 | passwd | password (passwd) | -| testCryptoSwift.swift:222:9:222:9 | passwd | testCryptoSwift.swift:222:9:222:9 | passwd | testCryptoSwift.swift:222:9:222:9 | passwd | Insecure hashing algorithm (SHA1) depends on $@. | testCryptoSwift.swift:222:9:222:9 | passwd | password (passwd) | -| testCryptoSwift.swift:225:9:225:9 | passwd | testCryptoSwift.swift:225:9:225:9 | passwd | testCryptoSwift.swift:225:9:225:9 | passwd | Insecure hashing algorithm (SHA512) depends on $@. | testCryptoSwift.swift:225:9:225:9 | passwd | password (passwd) | -| testCryptoSwift.swift:228:9:228:9 | passwd | testCryptoSwift.swift:228:9:228:9 | passwd | testCryptoSwift.swift:228:9:228:9 | passwd | Insecure hashing algorithm (SHA2) depends on $@. | testCryptoSwift.swift:228:9:228:9 | passwd | password (passwd) | -| testCryptoSwift.swift:231:9:231:9 | passwd | testCryptoSwift.swift:231:9:231:9 | passwd | testCryptoSwift.swift:231:9:231:9 | passwd | Insecure hashing algorithm (SHA3) depends on $@. | testCryptoSwift.swift:231:9:231:9 | passwd | password (passwd) | diff --git a/swift/ql/test/query-tests/Security/CWE-328/WeakPasswordHashing.qlref b/swift/ql/test/query-tests/Security/CWE-328/WeakPasswordHashing.qlref index b2cfaab1f5c..24744b4a425 100644 --- a/swift/ql/test/query-tests/Security/CWE-328/WeakPasswordHashing.qlref +++ b/swift/ql/test/query-tests/Security/CWE-328/WeakPasswordHashing.qlref @@ -1 +1,2 @@ -queries/Security/CWE-328/WeakPasswordHashing.ql +query: queries/Security/CWE-328/WeakPasswordHashing.ql +postprocess: utils/test/InlineExpectationsTestQuery.ql diff --git a/swift/ql/test/query-tests/Security/CWE-328/WeakSensitiveDataHashing.qlref b/swift/ql/test/query-tests/Security/CWE-328/WeakSensitiveDataHashing.qlref index 85270fde299..d76eeef6c2f 100644 --- a/swift/ql/test/query-tests/Security/CWE-328/WeakSensitiveDataHashing.qlref +++ b/swift/ql/test/query-tests/Security/CWE-328/WeakSensitiveDataHashing.qlref @@ -1 +1,2 @@ -queries/Security/CWE-328/WeakSensitiveDataHashing.ql +query: queries/Security/CWE-328/WeakSensitiveDataHashing.ql +postprocess: utils/test/InlineExpectationsTestQuery.ql diff --git a/swift/ql/test/query-tests/Security/CWE-328/testCryptoKit.swift b/swift/ql/test/query-tests/Security/CWE-328/testCryptoKit.swift index 6869805e65a..d2faf881224 100644 --- a/swift/ql/test/query-tests/Security/CWE-328/testCryptoKit.swift +++ b/swift/ql/test/query-tests/Security/CWE-328/testCryptoKit.swift @@ -81,43 +81,43 @@ enum Insecure { // --- tests --- func testHashMethods(passwd : UnsafeRawBufferPointer, cert: String, encrypted_passwd : String, account_no : String, credit_card_no : String) { - var hash = Crypto.Insecure.MD5.hash(data: passwd) // BAD - hash = Crypto.Insecure.MD5.hash(bufferPointer: passwd) // BAD - hash = Crypto.Insecure.MD5.hash(data: cert) // BAD + var hash = Crypto.Insecure.MD5.hash(data: passwd) // $ Alert[swift/weak-password-hashing] + hash = Crypto.Insecure.MD5.hash(bufferPointer: passwd) // $ Alert[swift/weak-password-hashing] + hash = Crypto.Insecure.MD5.hash(data: cert) // $ Alert[swift/weak-sensitive-data-hashing] hash = Crypto.Insecure.MD5.hash(data: encrypted_passwd) // GOOD (not sensitive) - hash = Crypto.Insecure.MD5.hash(data: account_no) // BAD - hash = Crypto.Insecure.MD5.hash(data: credit_card_no) // BAD + hash = Crypto.Insecure.MD5.hash(data: account_no) // $ Alert[swift/weak-sensitive-data-hashing] + hash = Crypto.Insecure.MD5.hash(data: credit_card_no) // $ Alert[swift/weak-sensitive-data-hashing] - hash = Insecure.MD5.hash(data: passwd) // BAD - hash = Insecure.MD5.hash(bufferPointer: passwd) // BAD - hash = Insecure.MD5.hash(data: cert) // BAD + hash = Insecure.MD5.hash(data: passwd) // $ Alert[swift/weak-password-hashing] + hash = Insecure.MD5.hash(bufferPointer: passwd) // $ Alert[swift/weak-password-hashing] + hash = Insecure.MD5.hash(data: cert) // $ Alert[swift/weak-sensitive-data-hashing] hash = Insecure.MD5.hash(data: encrypted_passwd) // GOOD (not sensitive) - hash = Insecure.MD5.hash(data: account_no) // BAD - hash = Insecure.MD5.hash(data: credit_card_no) // BAD + hash = Insecure.MD5.hash(data: account_no) // $ Alert[swift/weak-sensitive-data-hashing] + hash = Insecure.MD5.hash(data: credit_card_no) // $ Alert[swift/weak-sensitive-data-hashing] - hash = Crypto.Insecure.SHA1.hash(data: passwd) // BAD - hash = Crypto.Insecure.SHA1.hash(bufferPointer: passwd) // BAD - hash = Crypto.Insecure.SHA1.hash(data: cert) // BAD + hash = Crypto.Insecure.SHA1.hash(data: passwd) // $ Alert[swift/weak-password-hashing] + hash = Crypto.Insecure.SHA1.hash(bufferPointer: passwd) // $ Alert[swift/weak-password-hashing] + hash = Crypto.Insecure.SHA1.hash(data: cert) // $ Alert[swift/weak-sensitive-data-hashing] hash = Crypto.Insecure.SHA1.hash(data: encrypted_passwd) // GOOD (not sensitive) - hash = Crypto.Insecure.SHA1.hash(data: account_no) // BAD - hash = Crypto.Insecure.SHA1.hash(data: credit_card_no) // BAD + hash = Crypto.Insecure.SHA1.hash(data: account_no) // $ Alert[swift/weak-sensitive-data-hashing] + hash = Crypto.Insecure.SHA1.hash(data: credit_card_no) // $ Alert[swift/weak-sensitive-data-hashing] - hash = Crypto.SHA256.hash(data: passwd) // BAD, not a computationally expensive hash - hash = Crypto.SHA256.hash(bufferPointer: passwd) // BAD, not a computationally expensive hash + hash = Crypto.SHA256.hash(data: passwd) // $ Alert[swift/weak-password-hashing] // BAD, not a computationally expensive hash + hash = Crypto.SHA256.hash(bufferPointer: passwd) // $ Alert[swift/weak-password-hashing] // BAD, not a computationally expensive hash hash = Crypto.SHA256.hash(data: cert) // GOOD, computationally expensive hash not required hash = Crypto.SHA256.hash(data: encrypted_passwd) // GOOD, not sensitive hash = Crypto.SHA256.hash(data: account_no) // GOOD, computationally expensive hash not required hash = Crypto.SHA256.hash(data: credit_card_no) // GOOD, computationally expensive hash not required - hash = Crypto.SHA384.hash(data: passwd) // BAD, not a computationally expensive hash - hash = Crypto.SHA384.hash(bufferPointer: passwd) // BAD, not a computationally expensive hash + hash = Crypto.SHA384.hash(data: passwd) // $ Alert[swift/weak-password-hashing] // BAD, not a computationally expensive hash + hash = Crypto.SHA384.hash(bufferPointer: passwd) // $ Alert[swift/weak-password-hashing] // BAD, not a computationally expensive hash hash = Crypto.SHA384.hash(data: cert) // GOOD, computationally expensive hash not required hash = Crypto.SHA384.hash(data: encrypted_passwd) // GOOD, not sensitive hash = Crypto.SHA384.hash(data: account_no) // GOOD, computationally expensive hash not required hash = Crypto.SHA384.hash(data: credit_card_no) // GOOD, computationally expensive hash not required - hash = Crypto.SHA512.hash(data: passwd) // BAD, not a computationally expensive hash - hash = Crypto.SHA512.hash(bufferPointer: passwd) // BAD, not a computationally expensive hash + hash = Crypto.SHA512.hash(data: passwd) // $ Alert[swift/weak-password-hashing] // BAD, not a computationally expensive hash + hash = Crypto.SHA512.hash(bufferPointer: passwd) // $ Alert[swift/weak-password-hashing] // BAD, not a computationally expensive hash hash = Crypto.SHA512.hash(data: cert) // GOOD, computationally expensive hash not required hash = Crypto.SHA512.hash(data: encrypted_passwd) // GOOD, not sensitive hash = Crypto.SHA512.hash(data: account_no) // GOOD, computationally expensive hash not required @@ -126,25 +126,25 @@ func testHashMethods(passwd : UnsafeRawBufferPointer, cert: String, encrypted_pa func testMD5UpdateWithData(passwd : String, cert: String, encrypted_passwd : String, account_no : String, credit_card_no : String) { var hash = Crypto.Insecure.MD5() - hash.update(data: passwd) // BAD - hash.update(data: cert) // BAD + hash.update(data: passwd) // $ Alert[swift/weak-password-hashing] + hash.update(data: cert) // $ Alert[swift/weak-sensitive-data-hashing] hash.update(data: encrypted_passwd) // GOOD (not sensitive) - hash.update(data: account_no) // BAD - hash.update(data: credit_card_no) // BAD + hash.update(data: account_no) // $ Alert[swift/weak-sensitive-data-hashing] + hash.update(data: credit_card_no) // $ Alert[swift/weak-sensitive-data-hashing] } func testSHA1UpdateWithData(passwd : String, cert: String, encrypted_passwd : String, account_no : String, credit_card_no : String) { var hash = Crypto.Insecure.SHA1() - hash.update(data: passwd) // BAD - hash.update(data: cert) // BAD + hash.update(data: passwd) // $ Alert[swift/weak-password-hashing] + hash.update(data: cert) // $ Alert[swift/weak-sensitive-data-hashing] hash.update(data: encrypted_passwd) // GOOD (not sensitive) - hash.update(data: account_no) // BAD - hash.update(data: credit_card_no) // BAD + hash.update(data: account_no) // $ Alert[swift/weak-sensitive-data-hashing] + hash.update(data: credit_card_no) // $ Alert[swift/weak-sensitive-data-hashing] } func testSHA256UpdateWithData(passwd : String, cert: String, encrypted_passwd : String, account_no : String, credit_card_no : String) { var hash = Crypto.SHA256() - hash.update(data: passwd) // BAD, not a computationally expensive hash + hash.update(data: passwd) // $ Alert[swift/weak-password-hashing] // BAD, not a computationally expensive hash hash.update(data: cert) // GOOD hash.update(data: encrypted_passwd) // GOOD (not sensitive) hash.update(data: account_no) // GOOD @@ -153,7 +153,7 @@ func testSHA256UpdateWithData(passwd : String, cert: String, encrypted_passwd : func testSHA384UpdateWithData(passwd : String, cert: String, encrypted_passwd : String, account_no : String, credit_card_no : String) { var hash = Crypto.SHA384() - hash.update(data: passwd) // BAD, not a computationally expensive hash + hash.update(data: passwd) // $ Alert[swift/weak-password-hashing] // BAD, not a computationally expensive hash hash.update(data: cert) // GOOD hash.update(data: encrypted_passwd) // GOOD (not sensitive) hash.update(data: account_no) // GOOD @@ -162,7 +162,7 @@ func testSHA384UpdateWithData(passwd : String, cert: String, encrypted_passwd : func testSHA512UpdateWithData(passwd : String, cert: String, encrypted_passwd : String, account_no : String, credit_card_no : String) { var hash = Crypto.SHA512() - hash.update(data: passwd) // BAD, not a computationally expensive hash + hash.update(data: passwd) // $ Alert[swift/weak-password-hashing] // BAD, not a computationally expensive hash hash.update(data: cert) // GOOD hash.update(data: encrypted_passwd) // GOOD (not sensitive) hash.update(data: account_no) // GOOD @@ -171,25 +171,25 @@ func testSHA512UpdateWithData(passwd : String, cert: String, encrypted_passwd : func testMD5UpdateWithUnsafeRawBufferPointer(passwd : UnsafeRawBufferPointer, cert: UnsafeRawBufferPointer, encrypted_passwd : UnsafeRawBufferPointer, account_no : UnsafeRawBufferPointer, credit_card_no : UnsafeRawBufferPointer) { var hash = Crypto.Insecure.MD5() - hash.update(bufferPointer: passwd) // BAD - hash.update(bufferPointer: cert) // BAD + hash.update(bufferPointer: passwd) // $ Alert[swift/weak-password-hashing] + hash.update(bufferPointer: cert) // $ Alert[swift/weak-sensitive-data-hashing] hash.update(bufferPointer: encrypted_passwd) // GOOD (not sensitive) - hash.update(bufferPointer: account_no) // BAD - hash.update(bufferPointer: credit_card_no) // BAD + hash.update(bufferPointer: account_no) // $ Alert[swift/weak-sensitive-data-hashing] + hash.update(bufferPointer: credit_card_no) // $ Alert[swift/weak-sensitive-data-hashing] } func testSHA1UpdateWithUnsafeRawBufferPointer(passwd : UnsafeRawBufferPointer, cert: UnsafeRawBufferPointer, encrypted_passwd : UnsafeRawBufferPointer, account_no : UnsafeRawBufferPointer, credit_card_no : UnsafeRawBufferPointer) { var hash = Crypto.Insecure.SHA1() - hash.update(bufferPointer: passwd) // BAD - hash.update(bufferPointer: cert) // BAD + hash.update(bufferPointer: passwd) // $ Alert[swift/weak-password-hashing] + hash.update(bufferPointer: cert) // $ Alert[swift/weak-sensitive-data-hashing] hash.update(bufferPointer: encrypted_passwd) // GOOD (not sensitive) - hash.update(bufferPointer: account_no) // BAD - hash.update(bufferPointer: credit_card_no) // BAD + hash.update(bufferPointer: account_no) // $ Alert[swift/weak-sensitive-data-hashing] + hash.update(bufferPointer: credit_card_no) // $ Alert[swift/weak-sensitive-data-hashing] } func testSHA256UpdateWithUnsafeRawBufferPointer(passwd : UnsafeRawBufferPointer, cert: UnsafeRawBufferPointer, encrypted_passwd : UnsafeRawBufferPointer, account_no : UnsafeRawBufferPointer, credit_card_no : UnsafeRawBufferPointer) { var hash = Crypto.SHA256() - hash.update(bufferPointer: passwd) // BAD, not a computationally expensive hash + hash.update(bufferPointer: passwd) // $ Alert[swift/weak-password-hashing] // BAD, not a computationally expensive hash hash.update(bufferPointer: cert) // GOOD hash.update(bufferPointer: encrypted_passwd) // GOOD (not sensitive) hash.update(bufferPointer: account_no) // GOOD @@ -198,7 +198,7 @@ func testSHA256UpdateWithUnsafeRawBufferPointer(passwd : UnsafeRawBufferPointer, func testSHA384UpdateWithUnsafeRawBufferPointer(passwd : UnsafeRawBufferPointer, cert: UnsafeRawBufferPointer, encrypted_passwd : UnsafeRawBufferPointer, account_no : UnsafeRawBufferPointer, credit_card_no : UnsafeRawBufferPointer) { var hash = Crypto.SHA384() - hash.update(bufferPointer: passwd) // BAD, not a computationally expensive hash + hash.update(bufferPointer: passwd) // $ Alert[swift/weak-password-hashing] // BAD, not a computationally expensive hash hash.update(bufferPointer: cert) // GOOD hash.update(bufferPointer: encrypted_passwd) // GOOD (not sensitive) hash.update(bufferPointer: account_no) // GOOD @@ -207,7 +207,7 @@ func testSHA384UpdateWithUnsafeRawBufferPointer(passwd : UnsafeRawBufferPointer, func testSHA512UpdateWithUnsafeRawBufferPointer(passwd : UnsafeRawBufferPointer, cert: UnsafeRawBufferPointer, encrypted_passwd : UnsafeRawBufferPointer, account_no : UnsafeRawBufferPointer, credit_card_no : UnsafeRawBufferPointer) { var hash = Crypto.SHA512() - hash.update(bufferPointer: passwd) // BAD, not a computationally expensive hash + hash.update(bufferPointer: passwd) // $ Alert[swift/weak-password-hashing] // BAD, not a computationally expensive hash hash.update(bufferPointer: cert) // GOOD hash.update(bufferPointer: encrypted_passwd) // GOOD (not sensitive) hash.update(bufferPointer: account_no) // GOOD @@ -217,30 +217,30 @@ func testSHA512UpdateWithUnsafeRawBufferPointer(passwd : UnsafeRawBufferPointer, func testBadExample(passwordString: String) { // this is the "bad" example from the .qhelp let passwordData = Data(passwordString.utf8) - let passwordHash = Crypto.SHA512.hash(data: passwordData) // BAD, not a computationally expensive hash + let passwordHash = Crypto.SHA512.hash(data: passwordData) // $ Alert[swift/weak-password-hashing] // BAD, not a computationally expensive hash // ... - if Crypto.SHA512.hash(data: Data(passwordString.utf8)) == passwordHash { // BAD, not a computationally expensive hash + if Crypto.SHA512.hash(data: Data(passwordString.utf8)) == passwordHash { // $ Alert[swift/weak-password-hashing] // BAD, not a computationally expensive hash // ... } } func testWithFlowAndMetatypes(cardNumber: String) { - let value1 = Data(cardNumber.utf8); - let _digest1 = Insecure.MD5.hash(data: value1); // BAD + let value1 = Data(cardNumber.utf8); // $ Source[swift/weak-sensitive-data-hashing] + let _digest1 = Insecure.MD5.hash(data: value1); // $ Alert[swift/weak-sensitive-data-hashing] - let value2 = Data(cardNumber.utf8); + let value2 = Data(cardNumber.utf8); // $ Source[swift/weak-sensitive-data-hashing] let hasher2 = Insecure.MD5.self; // metatype - let _digest2 = hasher2.hash(data: value2); // BAD + let _digest2 = hasher2.hash(data: value2); // $ Alert[swift/weak-sensitive-data-hashing] - let value3 = Data(cardNumber.utf8); - let _digest3 = (Insecure.MD5.self).hash(data: value3); // BAD + let value3 = Data(cardNumber.utf8); // $ Source[swift/weak-sensitive-data-hashing] + let _digest3 = (Insecure.MD5.self).hash(data: value3); // $ Alert[swift/weak-sensitive-data-hashing] - let value4 = Data(cardNumber.utf8); + let value4 = Data(cardNumber.utf8); // $ Source[swift/weak-sensitive-data-hashing] testReceiver1(value: value4); - let value5 = Data(cardNumber.utf8); + let value5 = Data(cardNumber.utf8); // $ Source[swift/weak-sensitive-data-hashing] testReceiver2(hasher: Insecure.MD5.self, value: value5); let value6 = Data(cardNumber.utf8); @@ -248,13 +248,13 @@ func testWithFlowAndMetatypes(cardNumber: String) { } func testReceiver1(value: Data) { - let _digest = Insecure.MD5.hash(data: value); // BAD + let _digest = Insecure.MD5.hash(data: value); // $ Alert[swift/weak-sensitive-data-hashing] } func testReceiver2(hasher: Insecure.MD5.Type, value: Data) { - let _digest = hasher.hash(data: value); // BAD + let _digest = hasher.hash(data: value); // $ Alert[swift/weak-sensitive-data-hashing] } func testReceiver3(hasher: H.Type, value: Data) { - let _digest = hasher.hash(data: value); // BAD [NOT DETECTED] + let _digest = hasher.hash(data: value); // $ MISSING: Alert[swift/weak-sensitive-data-hashing] // BAD [NOT DETECTED] } diff --git a/swift/ql/test/query-tests/Security/CWE-328/testCryptoSwift.swift b/swift/ql/test/query-tests/Security/CWE-328/testCryptoSwift.swift index 15043bc15f6..a6f4584230e 100644 --- a/swift/ql/test/query-tests/Security/CWE-328/testCryptoSwift.swift +++ b/swift/ql/test/query-tests/Security/CWE-328/testCryptoSwift.swift @@ -150,83 +150,83 @@ extension String { func testArrays(harmlessArray: Array, phoneNumberArray: Array, passwdArray: Array) { _ = MD5().calculate(for: harmlessArray) // GOOD (not sensitive) - _ = MD5().calculate(for: phoneNumberArray) // BAD - _ = MD5().calculate(for: passwdArray) // BAD + _ = MD5().calculate(for: phoneNumberArray) // $ Alert[swift/weak-sensitive-data-hashing] + _ = MD5().calculate(for: passwdArray) // $ Alert[swift/weak-password-hashing] _ = SHA1().calculate(for: harmlessArray) // GOOD (not sensitive) - _ = SHA1().calculate(for: phoneNumberArray) // BAD - _ = SHA1().calculate(for: passwdArray) // BAD + _ = SHA1().calculate(for: phoneNumberArray) // $ Alert[swift/weak-sensitive-data-hashing] + _ = SHA1().calculate(for: passwdArray) // $ Alert[swift/weak-password-hashing] _ = SHA2(variant: .sha512).calculate(for: harmlessArray) // GOOD _ = SHA2(variant: .sha512).calculate(for: phoneNumberArray) // GOOD - _ = SHA2(variant: .sha512).calculate(for: passwdArray) // BAD + _ = SHA2(variant: .sha512).calculate(for: passwdArray) // $ Alert[swift/weak-password-hashing] _ = SHA3(variant: .sha512).calculate(for: harmlessArray) // GOOD _ = SHA3(variant: .sha512).calculate(for: phoneNumberArray) // GOOD - _ = SHA3(variant: .sha512).calculate(for: passwdArray) // BAD + _ = SHA3(variant: .sha512).calculate(for: passwdArray) // $ Alert[swift/weak-password-hashing] _ = Digest.md5(harmlessArray) // GOOD (not sensitive) - _ = Digest.md5(phoneNumberArray) // BAD - _ = Digest.md5(passwdArray) // BAD + _ = Digest.md5(phoneNumberArray) // $ Alert[swift/weak-sensitive-data-hashing] + _ = Digest.md5(passwdArray) // $ Alert[swift/weak-password-hashing] _ = Digest.sha1(harmlessArray) // GOOD (not sensitive) - _ = Digest.sha1(phoneNumberArray) // BAD - _ = Digest.sha1(passwdArray) // BAD + _ = Digest.sha1(phoneNumberArray) // $ Alert[swift/weak-sensitive-data-hashing] + _ = Digest.sha1(passwdArray) // $ Alert[swift/weak-password-hashing] _ = Digest.sha512(harmlessArray) // GOOD (not sensitive) _ = Digest.sha512(phoneNumberArray) // GOOD - _ = Digest.sha512(passwdArray) // BAD + _ = Digest.sha512(passwdArray) // $ Alert[swift/weak-password-hashing] _ = Digest.sha2(harmlessArray, variant: .sha512) // GOOD (not sensitive) _ = Digest.sha2(phoneNumberArray, variant: .sha512) // GOOD - _ = Digest.sha2(passwdArray, variant: .sha512) // BAD + _ = Digest.sha2(passwdArray, variant: .sha512) // $ Alert[swift/weak-password-hashing] _ = Digest.sha3(harmlessArray, variant: .sha512) // GOOD (not sensitive) _ = Digest.sha3(phoneNumberArray, variant: .sha512) // GOOD - _ = Digest.sha3(passwdArray, variant: .sha512) // BAD + _ = Digest.sha3(passwdArray, variant: .sha512) // $ Alert[swift/weak-password-hashing] _ = harmlessArray.md5() // GOOD (not sensitive) - _ = phoneNumberArray.md5() // BAD - _ = passwdArray.md5() // BAD + _ = phoneNumberArray.md5() // $ Alert[swift/weak-sensitive-data-hashing] + _ = passwdArray.md5() // $ Alert[swift/weak-password-hashing] _ = harmlessArray.sha1() // GOOD (not sensitive) - _ = phoneNumberArray.sha1() // BAD - _ = passwdArray.sha1() // BAD + _ = phoneNumberArray.sha1() // $ Alert[swift/weak-sensitive-data-hashing] + _ = passwdArray.sha1() // $ Alert[swift/weak-password-hashing] _ = harmlessArray.sha512() // GOOD _ = phoneNumberArray.sha512() // GOOD - _ = passwdArray.sha512() // BAD + _ = passwdArray.sha512() // $ Alert[swift/weak-password-hashing] _ = harmlessArray.sha2(.sha512) // GOOD _ = phoneNumberArray.sha2(.sha512) // GOOD - _ = passwdArray.sha2(.sha512) // BAD + _ = passwdArray.sha2(.sha512) // $ Alert[swift/weak-password-hashing] _ = harmlessArray.sha3(.sha512) // GOOD _ = phoneNumberArray.sha3(.sha512) // GOOD - _ = passwdArray.sha3(.sha512) // BAD + _ = passwdArray.sha3(.sha512) // $ Alert[swift/weak-password-hashing] } func testData(harmlessData: Data, medicalData: Data, passwdData: Data) { _ = harmlessData.md5() // GOOD (not sensitive) - _ = medicalData.md5() // BAD - _ = passwdData.md5() // BAD + _ = medicalData.md5() // $ Alert[swift/weak-sensitive-data-hashing] + _ = passwdData.md5() // $ Alert[swift/weak-password-hashing] _ = harmlessData.sha1() // GOOD (not sensitive) - _ = medicalData.sha1() // BAD - _ = passwdData.sha1() // BAD + _ = medicalData.sha1() // $ Alert[swift/weak-sensitive-data-hashing] + _ = passwdData.sha1() // $ Alert[swift/weak-password-hashing] _ = harmlessData.sha512() // GOOD _ = medicalData.sha512() // GOOD - _ = passwdData.sha512() // BAD + _ = passwdData.sha512() // $ Alert[swift/weak-password-hashing] _ = harmlessData.sha2(.sha512) // GOOD _ = medicalData.sha2(.sha512) // GOOD - _ = passwdData.sha2(.sha512) // BAD + _ = passwdData.sha2(.sha512) // $ Alert[swift/weak-password-hashing] _ = harmlessData.sha3(.sha512) // GOOD _ = medicalData.sha3(.sha512) // GOOD - _ = passwdData.sha3(.sha512) // BAD + _ = passwdData.sha3(.sha512) // $ Alert[swift/weak-password-hashing] } func testStrings(creditCardNumber: String, passwd: String) { _ = "harmless".md5() // GOOD (not sensitive) - _ = creditCardNumber.md5() // BAD - _ = passwd.md5() // BAD + _ = creditCardNumber.md5() // $ Alert[swift/weak-sensitive-data-hashing] + _ = passwd.md5() // $ Alert[swift/weak-password-hashing] _ = "harmless".sha1() // GOOD (not sensitive) - _ = creditCardNumber.sha1() // BAD - _ = passwd.sha1() // BAD + _ = creditCardNumber.sha1() // $ Alert[swift/weak-sensitive-data-hashing] + _ = passwd.sha1() // $ Alert[swift/weak-password-hashing] _ = "harmless".sha512() // GOOD _ = creditCardNumber.sha512() // GOOD - _ = passwd.sha512() // BAD + _ = passwd.sha512() // $ Alert[swift/weak-password-hashing] _ = "harmless".sha2(.sha512) // GOOD _ = creditCardNumber.sha2(.sha512) // GOOD - _ = passwd.sha2(.sha512) // BAD + _ = passwd.sha2(.sha512) // $ Alert[swift/weak-password-hashing] _ = "harmless".sha3(.sha512) // GOOD _ = creditCardNumber.sha3(.sha512) // GOOD - _ = passwd.sha3(.sha512) // BAD + _ = passwd.sha3(.sha512) // $ Alert[swift/weak-password-hashing] } diff --git a/swift/ql/test/query-tests/Security/CWE-730/RegexInjection.expected b/swift/ql/test/query-tests/Security/CWE-730/RegexInjection.expected index 1a26f921197..04dafbd0b5e 100644 --- a/swift/ql/test/query-tests/Security/CWE-730/RegexInjection.expected +++ b/swift/ql/test/query-tests/Security/CWE-730/RegexInjection.expected @@ -1,3 +1,27 @@ +#select +| tests.swift:101:16:101:16 | taintedString | tests.swift:95:22:95:46 | call to String.init(contentsOf:) | tests.swift:101:16:101:16 | taintedString | This regular expression is constructed from a $@. | tests.swift:95:22:95:46 | call to String.init(contentsOf:) | user-provided value | +| tests.swift:104:16:104:40 | ... .+(_:_:) ... | tests.swift:95:22:95:46 | call to String.init(contentsOf:) | tests.swift:104:16:104:40 | ... .+(_:_:) ... | This regular expression is constructed from a $@. | tests.swift:95:22:95:46 | call to String.init(contentsOf:) | user-provided value | +| tests.swift:106:16:106:16 | "..." | tests.swift:95:22:95:46 | call to String.init(contentsOf:) | tests.swift:106:16:106:16 | "..." | This regular expression is constructed from a $@. | tests.swift:95:22:95:46 | call to String.init(contentsOf:) | user-provided value | +| tests.swift:109:16:109:39 | ... ? ... : ... | tests.swift:95:22:95:46 | call to String.init(contentsOf:) | tests.swift:109:16:109:39 | ... ? ... : ... | This regular expression is constructed from a $@. | tests.swift:95:22:95:46 | call to String.init(contentsOf:) | user-provided value | +| tests.swift:110:16:110:37 | ... ? ... : ... | tests.swift:95:22:95:46 | call to String.init(contentsOf:) | tests.swift:110:16:110:37 | ... ? ... : ... | This regular expression is constructed from a $@. | tests.swift:95:22:95:46 | call to String.init(contentsOf:) | user-provided value | +| tests.swift:113:24:113:24 | taintedString | tests.swift:95:22:95:46 | call to String.init(contentsOf:) | tests.swift:113:24:113:24 | taintedString | This regular expression is constructed from a $@. | tests.swift:95:22:95:46 | call to String.init(contentsOf:) | user-provided value | +| tests.swift:114:45:114:45 | taintedString | tests.swift:95:22:95:46 | call to String.init(contentsOf:) | tests.swift:114:45:114:45 | taintedString | This regular expression is constructed from a $@. | tests.swift:95:22:95:46 | call to String.init(contentsOf:) | user-provided value | +| tests.swift:120:19:120:19 | taintedString | tests.swift:95:22:95:46 | call to String.init(contentsOf:) | tests.swift:120:19:120:19 | taintedString | This regular expression is constructed from a $@. | tests.swift:95:22:95:46 | call to String.init(contentsOf:) | user-provided value | +| tests.swift:126:40:126:40 | taintedString | tests.swift:95:22:95:46 | call to String.init(contentsOf:) | tests.swift:126:40:126:40 | taintedString | This regular expression is constructed from a $@. | tests.swift:95:22:95:46 | call to String.init(contentsOf:) | user-provided value | +| tests.swift:131:39:131:39 | taintedString | tests.swift:95:22:95:46 | call to String.init(contentsOf:) | tests.swift:131:39:131:39 | taintedString | This regular expression is constructed from a $@. | tests.swift:95:22:95:46 | call to String.init(contentsOf:) | user-provided value | +| tests.swift:137:40:137:40 | taintedString | tests.swift:95:22:95:46 | call to String.init(contentsOf:) | tests.swift:137:40:137:40 | taintedString | This regular expression is constructed from a $@. | tests.swift:95:22:95:46 | call to String.init(contentsOf:) | user-provided value | +| tests.swift:144:16:144:16 | remoteInput | tests.swift:95:22:95:46 | call to String.init(contentsOf:) | tests.swift:144:16:144:16 | remoteInput | This regular expression is constructed from a $@. | tests.swift:95:22:95:46 | call to String.init(contentsOf:) | user-provided value | +| tests.swift:147:39:147:39 | regexStr | tests.swift:95:22:95:46 | call to String.init(contentsOf:) | tests.swift:147:39:147:39 | regexStr | This regular expression is constructed from a $@. | tests.swift:95:22:95:46 | call to String.init(contentsOf:) | user-provided value | +| tests.swift:162:17:162:17 | taintedString | tests.swift:95:22:95:46 | call to String.init(contentsOf:) | tests.swift:162:17:162:17 | taintedString | This regular expression is constructed from a $@. | tests.swift:95:22:95:46 | call to String.init(contentsOf:) | user-provided value | +| tests.swift:164:17:164:17 | taintedString | tests.swift:95:22:95:46 | call to String.init(contentsOf:) | tests.swift:164:17:164:17 | taintedString | This regular expression is constructed from a $@. | tests.swift:95:22:95:46 | call to String.init(contentsOf:) | user-provided value | +| tests.swift:167:17:167:17 | taintedString | tests.swift:95:22:95:46 | call to String.init(contentsOf:) | tests.swift:167:17:167:17 | taintedString | This regular expression is constructed from a $@. | tests.swift:95:22:95:46 | call to String.init(contentsOf:) | user-provided value | +| tests.swift:170:17:170:17 | taintedString | tests.swift:95:22:95:46 | call to String.init(contentsOf:) | tests.swift:170:17:170:17 | taintedString | This regular expression is constructed from a $@. | tests.swift:95:22:95:46 | call to String.init(contentsOf:) | user-provided value | +| tests.swift:173:17:173:17 | taintedString | tests.swift:95:22:95:46 | call to String.init(contentsOf:) | tests.swift:173:17:173:17 | taintedString | This regular expression is constructed from a $@. | tests.swift:95:22:95:46 | call to String.init(contentsOf:) | user-provided value | +| tests.swift:176:17:176:17 | taintedString | tests.swift:95:22:95:46 | call to String.init(contentsOf:) | tests.swift:176:17:176:17 | taintedString | This regular expression is constructed from a $@. | tests.swift:95:22:95:46 | call to String.init(contentsOf:) | user-provided value | +| tests.swift:179:17:179:17 | taintedString | tests.swift:95:22:95:46 | call to String.init(contentsOf:) | tests.swift:179:17:179:17 | taintedString | This regular expression is constructed from a $@. | tests.swift:95:22:95:46 | call to String.init(contentsOf:) | user-provided value | +| tests.swift:182:17:182:17 | taintedString | tests.swift:95:22:95:46 | call to String.init(contentsOf:) | tests.swift:182:17:182:17 | taintedString | This regular expression is constructed from a $@. | tests.swift:95:22:95:46 | call to String.init(contentsOf:) | user-provided value | +| tests.swift:185:17:185:17 | taintedString | tests.swift:95:22:95:46 | call to String.init(contentsOf:) | tests.swift:185:17:185:17 | taintedString | This regular expression is constructed from a $@. | tests.swift:95:22:95:46 | call to String.init(contentsOf:) | user-provided value | +| tests.swift:190:21:190:21 | taintedString | tests.swift:95:22:95:46 | call to String.init(contentsOf:) | tests.swift:190:21:190:21 | taintedString | This regular expression is constructed from a $@. | tests.swift:95:22:95:46 | call to String.init(contentsOf:) | user-provided value | edges | tests.swift:95:22:95:46 | call to String.init(contentsOf:) | tests.swift:101:16:101:16 | taintedString | provenance | | | tests.swift:95:22:95:46 | call to String.init(contentsOf:) | tests.swift:104:16:104:40 | ... .+(_:_:) ... | provenance | | @@ -48,27 +72,3 @@ nodes | tests.swift:185:17:185:17 | taintedString | semmle.label | taintedString | | tests.swift:190:21:190:21 | taintedString | semmle.label | taintedString | subpaths -#select -| tests.swift:101:16:101:16 | taintedString | tests.swift:95:22:95:46 | call to String.init(contentsOf:) | tests.swift:101:16:101:16 | taintedString | This regular expression is constructed from a $@. | tests.swift:95:22:95:46 | call to String.init(contentsOf:) | user-provided value | -| tests.swift:104:16:104:40 | ... .+(_:_:) ... | tests.swift:95:22:95:46 | call to String.init(contentsOf:) | tests.swift:104:16:104:40 | ... .+(_:_:) ... | This regular expression is constructed from a $@. | tests.swift:95:22:95:46 | call to String.init(contentsOf:) | user-provided value | -| tests.swift:106:16:106:16 | "..." | tests.swift:95:22:95:46 | call to String.init(contentsOf:) | tests.swift:106:16:106:16 | "..." | This regular expression is constructed from a $@. | tests.swift:95:22:95:46 | call to String.init(contentsOf:) | user-provided value | -| tests.swift:109:16:109:39 | ... ? ... : ... | tests.swift:95:22:95:46 | call to String.init(contentsOf:) | tests.swift:109:16:109:39 | ... ? ... : ... | This regular expression is constructed from a $@. | tests.swift:95:22:95:46 | call to String.init(contentsOf:) | user-provided value | -| tests.swift:110:16:110:37 | ... ? ... : ... | tests.swift:95:22:95:46 | call to String.init(contentsOf:) | tests.swift:110:16:110:37 | ... ? ... : ... | This regular expression is constructed from a $@. | tests.swift:95:22:95:46 | call to String.init(contentsOf:) | user-provided value | -| tests.swift:113:24:113:24 | taintedString | tests.swift:95:22:95:46 | call to String.init(contentsOf:) | tests.swift:113:24:113:24 | taintedString | This regular expression is constructed from a $@. | tests.swift:95:22:95:46 | call to String.init(contentsOf:) | user-provided value | -| tests.swift:114:45:114:45 | taintedString | tests.swift:95:22:95:46 | call to String.init(contentsOf:) | tests.swift:114:45:114:45 | taintedString | This regular expression is constructed from a $@. | tests.swift:95:22:95:46 | call to String.init(contentsOf:) | user-provided value | -| tests.swift:120:19:120:19 | taintedString | tests.swift:95:22:95:46 | call to String.init(contentsOf:) | tests.swift:120:19:120:19 | taintedString | This regular expression is constructed from a $@. | tests.swift:95:22:95:46 | call to String.init(contentsOf:) | user-provided value | -| tests.swift:126:40:126:40 | taintedString | tests.swift:95:22:95:46 | call to String.init(contentsOf:) | tests.swift:126:40:126:40 | taintedString | This regular expression is constructed from a $@. | tests.swift:95:22:95:46 | call to String.init(contentsOf:) | user-provided value | -| tests.swift:131:39:131:39 | taintedString | tests.swift:95:22:95:46 | call to String.init(contentsOf:) | tests.swift:131:39:131:39 | taintedString | This regular expression is constructed from a $@. | tests.swift:95:22:95:46 | call to String.init(contentsOf:) | user-provided value | -| tests.swift:137:40:137:40 | taintedString | tests.swift:95:22:95:46 | call to String.init(contentsOf:) | tests.swift:137:40:137:40 | taintedString | This regular expression is constructed from a $@. | tests.swift:95:22:95:46 | call to String.init(contentsOf:) | user-provided value | -| tests.swift:144:16:144:16 | remoteInput | tests.swift:95:22:95:46 | call to String.init(contentsOf:) | tests.swift:144:16:144:16 | remoteInput | This regular expression is constructed from a $@. | tests.swift:95:22:95:46 | call to String.init(contentsOf:) | user-provided value | -| tests.swift:147:39:147:39 | regexStr | tests.swift:95:22:95:46 | call to String.init(contentsOf:) | tests.swift:147:39:147:39 | regexStr | This regular expression is constructed from a $@. | tests.swift:95:22:95:46 | call to String.init(contentsOf:) | user-provided value | -| tests.swift:162:17:162:17 | taintedString | tests.swift:95:22:95:46 | call to String.init(contentsOf:) | tests.swift:162:17:162:17 | taintedString | This regular expression is constructed from a $@. | tests.swift:95:22:95:46 | call to String.init(contentsOf:) | user-provided value | -| tests.swift:164:17:164:17 | taintedString | tests.swift:95:22:95:46 | call to String.init(contentsOf:) | tests.swift:164:17:164:17 | taintedString | This regular expression is constructed from a $@. | tests.swift:95:22:95:46 | call to String.init(contentsOf:) | user-provided value | -| tests.swift:167:17:167:17 | taintedString | tests.swift:95:22:95:46 | call to String.init(contentsOf:) | tests.swift:167:17:167:17 | taintedString | This regular expression is constructed from a $@. | tests.swift:95:22:95:46 | call to String.init(contentsOf:) | user-provided value | -| tests.swift:170:17:170:17 | taintedString | tests.swift:95:22:95:46 | call to String.init(contentsOf:) | tests.swift:170:17:170:17 | taintedString | This regular expression is constructed from a $@. | tests.swift:95:22:95:46 | call to String.init(contentsOf:) | user-provided value | -| tests.swift:173:17:173:17 | taintedString | tests.swift:95:22:95:46 | call to String.init(contentsOf:) | tests.swift:173:17:173:17 | taintedString | This regular expression is constructed from a $@. | tests.swift:95:22:95:46 | call to String.init(contentsOf:) | user-provided value | -| tests.swift:176:17:176:17 | taintedString | tests.swift:95:22:95:46 | call to String.init(contentsOf:) | tests.swift:176:17:176:17 | taintedString | This regular expression is constructed from a $@. | tests.swift:95:22:95:46 | call to String.init(contentsOf:) | user-provided value | -| tests.swift:179:17:179:17 | taintedString | tests.swift:95:22:95:46 | call to String.init(contentsOf:) | tests.swift:179:17:179:17 | taintedString | This regular expression is constructed from a $@. | tests.swift:95:22:95:46 | call to String.init(contentsOf:) | user-provided value | -| tests.swift:182:17:182:17 | taintedString | tests.swift:95:22:95:46 | call to String.init(contentsOf:) | tests.swift:182:17:182:17 | taintedString | This regular expression is constructed from a $@. | tests.swift:95:22:95:46 | call to String.init(contentsOf:) | user-provided value | -| tests.swift:185:17:185:17 | taintedString | tests.swift:95:22:95:46 | call to String.init(contentsOf:) | tests.swift:185:17:185:17 | taintedString | This regular expression is constructed from a $@. | tests.swift:95:22:95:46 | call to String.init(contentsOf:) | user-provided value | -| tests.swift:190:21:190:21 | taintedString | tests.swift:95:22:95:46 | call to String.init(contentsOf:) | tests.swift:190:21:190:21 | taintedString | This regular expression is constructed from a $@. | tests.swift:95:22:95:46 | call to String.init(contentsOf:) | user-provided value | diff --git a/swift/ql/test/query-tests/Security/CWE-730/RegexInjection.qlref b/swift/ql/test/query-tests/Security/CWE-730/RegexInjection.qlref index 6171cd82074..edd571a6692 100644 --- a/swift/ql/test/query-tests/Security/CWE-730/RegexInjection.qlref +++ b/swift/ql/test/query-tests/Security/CWE-730/RegexInjection.qlref @@ -1 +1,2 @@ -queries/Security/CWE-730/RegexInjection.ql +query: queries/Security/CWE-730/RegexInjection.ql +postprocess: utils/test/InlineExpectationsTestQuery.ql diff --git a/swift/ql/test/query-tests/Security/CWE-730/tests.swift b/swift/ql/test/query-tests/Security/CWE-730/tests.swift index 234821d46ac..0fe6b5e9802 100644 --- a/swift/ql/test/query-tests/Security/CWE-730/tests.swift +++ b/swift/ql/test/query-tests/Security/CWE-730/tests.swift @@ -92,59 +92,59 @@ extension String { func regexInjectionTests(cond: Bool, varString: String, myUrl: URL) throws { let constString = ".*" - let taintedString = String(contentsOf: myUrl) // tainted + let taintedString = String(contentsOf: myUrl) // $ Source // tainted // --- Regex --- _ = try Regex(constString).firstMatch(in: varString) _ = try Regex(varString).firstMatch(in: varString) - _ = try Regex(taintedString).firstMatch(in: varString) // BAD + _ = try Regex(taintedString).firstMatch(in: varString) // $ Alert _ = try Regex("(a|" + constString + ")").firstMatch(in: varString) - _ = try Regex("(a|" + taintedString + ")").firstMatch(in: varString) // BAD + _ = try Regex("(a|" + taintedString + ")").firstMatch(in: varString) // $ Alert _ = try Regex("(a|\(constString))").firstMatch(in: varString) - _ = try Regex("(a|\(taintedString))").firstMatch(in: varString) // BAD + _ = try Regex("(a|\(taintedString))").firstMatch(in: varString) // $ Alert _ = try Regex(cond ? constString : constString).firstMatch(in: varString) - _ = try Regex(cond ? taintedString : constString).firstMatch(in: varString) // BAD - _ = try Regex(cond ? constString : taintedString).firstMatch(in: varString) // BAD + _ = try Regex(cond ? taintedString : constString).firstMatch(in: varString) // $ Alert + _ = try Regex(cond ? constString : taintedString).firstMatch(in: varString) // $ Alert _ = try (cond ? Regex(constString) : Regex(constString)).firstMatch(in: varString) - _ = try (cond ? Regex(taintedString) : Regex(constString)).firstMatch(in: varString) // BAD - _ = try (cond ? Regex(constString) : Regex(taintedString)).firstMatch(in: varString) // BAD + _ = try (cond ? Regex(taintedString) : Regex(constString)).firstMatch(in: varString) // $ Alert + _ = try (cond ? Regex(constString) : Regex(taintedString)).firstMatch(in: varString) // $ Alert // --- RangeReplaceableCollection --- var inputVar = varString inputVar.replace(constString, with: "") - inputVar.replace(taintedString, with: "") // BAD + inputVar.replace(taintedString, with: "") // $ Alert inputVar.replace(constString, with: taintedString) // --- StringProtocol --- _ = inputVar.replacingOccurrences(of: constString, with: "", options: .regularExpression) - _ = inputVar.replacingOccurrences(of: taintedString, with: "", options: .regularExpression) // BAD + _ = inputVar.replacingOccurrences(of: taintedString, with: "", options: .regularExpression) // $ Alert // --- NSRegularExpression --- _ = try NSRegularExpression(pattern: constString).firstMatch(in: varString, range: NSMakeRange(0, varString.utf16.count)) - _ = try NSRegularExpression(pattern: taintedString).firstMatch(in: varString, range: NSMakeRange(0, varString.utf16.count)) // BAD + _ = try NSRegularExpression(pattern: taintedString).firstMatch(in: varString, range: NSMakeRange(0, varString.utf16.count)) // $ Alert // --- NSString --- let nsString = NSString(string: varString) _ = nsString.replacingOccurrences(of: constString, with: "", options: .regularExpression, range: NSMakeRange(0, nsString.length)) - _ = nsString.replacingOccurrences(of: taintedString, with: "", options: .regularExpression, range: NSMakeRange(0, nsString.length)) // BAD + _ = nsString.replacingOccurrences(of: taintedString, with: "", options: .regularExpression, range: NSMakeRange(0, nsString.length)) // $ Alert // --- from the qhelp --- let remoteInput = taintedString let myRegex = ".*" - _ = try Regex(remoteInput) // BAD + _ = try Regex(remoteInput) // $ Alert let regexStr = "abc|\(remoteInput)" - _ = try NSRegularExpression(pattern: regexStr) // BAD + _ = try NSRegularExpression(pattern: regexStr) // $ Alert _ = try Regex(myRegex) @@ -159,35 +159,35 @@ func regexInjectionTests(cond: Bool, varString: String, myUrl: URL) throws { let okSet: Set = ["abc", "def"] if (taintedString == okInput) { - _ = try Regex(taintedString).firstMatch(in: varString) // GOOD (effectively sanitized by the check) [FALSE POSITIVE] + _ = try Regex(taintedString).firstMatch(in: varString) // $ SPURIOUS: Alert // GOOD (effectively sanitized by the check) [FALSE POSITIVE] } else { - _ = try Regex(taintedString).firstMatch(in: varString) // BAD + _ = try Regex(taintedString).firstMatch(in: varString) // $ Alert } if (taintedString != okInput) { - _ = try Regex(taintedString).firstMatch(in: varString) // BAD + _ = try Regex(taintedString).firstMatch(in: varString) // $ Alert } if (varString == okInput) { - _ = try Regex(taintedString).firstMatch(in: varString) // BAD + _ = try Regex(taintedString).firstMatch(in: varString) // $ Alert } if (okInputs.contains(taintedString)) { - _ = try Regex(taintedString).firstMatch(in: varString) // GOOD (effectively sanitized by the check) [FALSE POSITIVE] + _ = try Regex(taintedString).firstMatch(in: varString) // $ SPURIOUS: Alert // GOOD (effectively sanitized by the check) [FALSE POSITIVE] } if (okInputs.firstIndex(of: taintedString) != nil) { - _ = try Regex(taintedString).firstMatch(in: varString) // GOOD (effectively sanitized by the check) [FALSE POSITIVE] + _ = try Regex(taintedString).firstMatch(in: varString) // $ SPURIOUS: Alert // GOOD (effectively sanitized by the check) [FALSE POSITIVE] } if let index = okInputs.firstIndex(of: taintedString) { - _ = try Regex(taintedString).firstMatch(in: varString) // GOOD (effectively sanitized by the check) [FALSE POSITIVE] + _ = try Regex(taintedString).firstMatch(in: varString) // $ SPURIOUS: Alert // GOOD (effectively sanitized by the check) [FALSE POSITIVE] } if let index = okInputs.index(of: taintedString) { - _ = try Regex(taintedString).firstMatch(in: varString) // GOOD (effectively sanitized by the check) [FALSE POSITIVE] + _ = try Regex(taintedString).firstMatch(in: varString) // $ SPURIOUS: Alert // GOOD (effectively sanitized by the check) [FALSE POSITIVE] } if (okSet.contains(taintedString)) { - _ = try Regex(taintedString).firstMatch(in: varString) // GOOD (effectively sanitized by the check) [FALSE POSITIVE] + _ = try Regex(taintedString).firstMatch(in: varString) // $ SPURIOUS: Alert // GOOD (effectively sanitized by the check) [FALSE POSITIVE] } // --- multiple evaluations --- - let re = try Regex(taintedString) // BAD + let re = try Regex(taintedString) // $ Alert _ = try re.firstMatch(in: varString) // (we only want to flag one location total) _ = try re.firstMatch(in: varString) } diff --git a/swift/ql/test/query-tests/Security/CWE-760/ConstantSalt.qlref b/swift/ql/test/query-tests/Security/CWE-760/ConstantSalt.qlref index 04aadc2161f..dd7c483b0af 100644 --- a/swift/ql/test/query-tests/Security/CWE-760/ConstantSalt.qlref +++ b/swift/ql/test/query-tests/Security/CWE-760/ConstantSalt.qlref @@ -1 +1,2 @@ -queries/Security/CWE-760/ConstantSalt.ql +query: queries/Security/CWE-760/ConstantSalt.ql +postprocess: utils/test/InlineExpectationsTestQuery.ql diff --git a/swift/ql/test/query-tests/Security/CWE-760/rncryptor.swift b/swift/ql/test/query-tests/Security/CWE-760/rncryptor.swift index 51265b16c45..6c0c3c00988 100644 --- a/swift/ql/test/query-tests/Security/CWE-760/rncryptor.swift +++ b/swift/ql/test/query-tests/Security/CWE-760/rncryptor.swift @@ -56,35 +56,35 @@ func test(myPassword: String) { let myIV = Data(0) let myRandomSalt1 = Data(getARandomString()) let myRandomSalt2 = Data(getARandomString()) - let myConstantSalt1 = Data("abcdef123456") - let myConstantSalt2 = Data(0) + let myConstantSalt1 = Data("abcdef123456") // $ Source + let myConstantSalt2 = Data(0) // $ Source let _ = myEncryptor.key(forPassword: myPassword, salt: myRandomSalt1, settings: myKeyDerivationSettings) // GOOD - let _ = myEncryptor.key(forPassword: myPassword, salt: myConstantSalt1, settings: myKeyDerivationSettings) // BAD + let _ = myEncryptor.key(forPassword: myPassword, salt: myConstantSalt1, settings: myKeyDerivationSettings) // $ Alert let _ = myEncryptor.keyForPassword(myPassword, salt: myRandomSalt2, settings: myKeyDerivationSettings) // GOOD - let _ = myEncryptor.keyForPassword(myPassword, salt: myConstantSalt2, settings: myKeyDerivationSettings) // BAD + let _ = myEncryptor.keyForPassword(myPassword, salt: myConstantSalt2, settings: myKeyDerivationSettings) // $ Alert let _ = RNEncryptor(settings: kRNCryptorAES256Settings, password: myPassword, iv: myIV, encryptionSalt: myRandomSalt1, hmacSalt: myRandomSalt2, handler: myHandler) // GOOD - let _ = RNEncryptor(settings: kRNCryptorAES256Settings, password: myPassword, iv: myIV, encryptionSalt: myConstantSalt1, hmacSalt: myRandomSalt2, handler: myHandler) // BAD - let _ = RNEncryptor(settings: kRNCryptorAES256Settings, password: myPassword, iv: myIV, encryptionSalt: myRandomSalt1, hmacSalt: myConstantSalt2, handler: myHandler) // BAD + let _ = RNEncryptor(settings: kRNCryptorAES256Settings, password: myPassword, iv: myIV, encryptionSalt: myConstantSalt1, hmacSalt: myRandomSalt2, handler: myHandler) // $ Alert + let _ = RNEncryptor(settings: kRNCryptorAES256Settings, password: myPassword, iv: myIV, encryptionSalt: myRandomSalt1, hmacSalt: myConstantSalt2, handler: myHandler) // $ Alert let _ = RNEncryptor(settings: kRNCryptorAES256Settings, password: myPassword, IV: myIV, encryptionSalt: myRandomSalt1, HMACSalt: myRandomSalt2, handler: myHandler) // GOOD - let _ = RNEncryptor(settings: kRNCryptorAES256Settings, password: myPassword, IV: myIV, encryptionSalt: myConstantSalt1, HMACSalt: myRandomSalt2, handler: myHandler) // BAD - let _ = RNEncryptor(settings: kRNCryptorAES256Settings, password: myPassword, IV: myIV, encryptionSalt: myRandomSalt1, HMACSalt: myConstantSalt2, handler: myHandler) // BAD + let _ = RNEncryptor(settings: kRNCryptorAES256Settings, password: myPassword, IV: myIV, encryptionSalt: myConstantSalt1, HMACSalt: myRandomSalt2, handler: myHandler) // $ Alert + let _ = RNEncryptor(settings: kRNCryptorAES256Settings, password: myPassword, IV: myIV, encryptionSalt: myRandomSalt1, HMACSalt: myConstantSalt2, handler: myHandler) // $ Alert let _ = try? myEncryptor.encryptData(myData, with: kRNCryptorAES256Settings, password: myPassword, iv: myIV, encryptionSalt: myRandomSalt1, hmacSalt: myRandomSalt2) // GOOD - let _ = try? myEncryptor.encryptData(myData, with: kRNCryptorAES256Settings, password: myPassword, iv: myIV, encryptionSalt: myConstantSalt1, hmacSalt: myRandomSalt2) // BAD - let _ = try? myEncryptor.encryptData(myData, with: kRNCryptorAES256Settings, password: myPassword, iv: myIV, encryptionSalt: myRandomSalt1, hmacSalt: myConstantSalt2) // BAD + let _ = try? myEncryptor.encryptData(myData, with: kRNCryptorAES256Settings, password: myPassword, iv: myIV, encryptionSalt: myConstantSalt1, hmacSalt: myRandomSalt2) // $ Alert + let _ = try? myEncryptor.encryptData(myData, with: kRNCryptorAES256Settings, password: myPassword, iv: myIV, encryptionSalt: myRandomSalt1, hmacSalt: myConstantSalt2) // $ Alert let _ = try? myEncryptor.encryptData(myData, withSettings: kRNCryptorAES256Settings, password: myPassword, IV: myIV, encryptionSalt: myRandomSalt1, HMACSalt: myRandomSalt2) // GOOD - let _ = try? myEncryptor.encryptData(myData, withSettings: kRNCryptorAES256Settings, password: myPassword, IV: myIV, encryptionSalt: myConstantSalt1, HMACSalt: myRandomSalt2) // BAD - let _ = try? myEncryptor.encryptData(myData, withSettings: kRNCryptorAES256Settings, password: myPassword, IV: myIV, encryptionSalt: myRandomSalt1, HMACSalt: myConstantSalt2) // BAD + let _ = try? myEncryptor.encryptData(myData, withSettings: kRNCryptorAES256Settings, password: myPassword, IV: myIV, encryptionSalt: myConstantSalt1, HMACSalt: myRandomSalt2) // $ Alert + let _ = try? myEncryptor.encryptData(myData, withSettings: kRNCryptorAES256Settings, password: myPassword, IV: myIV, encryptionSalt: myRandomSalt1, HMACSalt: myConstantSalt2) // $ Alert // appending constants let _ = myEncryptor.key(forPassword: myPassword, salt: Data(getARandomString() + getARandomString()), settings: myKeyDerivationSettings) // GOOD let _ = myEncryptor.key(forPassword: myPassword, salt: Data("123" + getARandomString()), settings: myKeyDerivationSettings) // GOOD let _ = myEncryptor.key(forPassword: myPassword, salt: Data(getARandomString() + "abc"), settings: myKeyDerivationSettings) // GOOD - let _ = myEncryptor.key(forPassword: myPassword, salt: Data("123" + "abc"), settings: myKeyDerivationSettings) // BAD (constant salt) [NOT DETECTED] + let _ = myEncryptor.key(forPassword: myPassword, salt: Data("123" + "abc"), settings: myKeyDerivationSettings) // $ MISSING: Alert // BAD (constant salt) [NOT DETECTED] let _ = myEncryptor.key(forPassword: myPassword, salt: Data("123\(getARandomString())abc"), settings: myKeyDerivationSettings) // GOOD - let _ = myEncryptor.key(forPassword: myPassword, salt: Data("123\("const"))abc"), settings: myKeyDerivationSettings) // BAD (constant salt) [NOT DETECTED] + let _ = myEncryptor.key(forPassword: myPassword, salt: Data("123\("const"))abc"), settings: myKeyDerivationSettings) // $ MISSING: Alert // BAD (constant salt) [NOT DETECTED] var myMutableString1 = "123" myMutableString1.append(getARandomString()) diff --git a/swift/ql/test/query-tests/Security/CWE-760/test.swift b/swift/ql/test/query-tests/Security/CWE-760/test.swift index 434e2daf6da..2ad979a1fbe 100644 --- a/swift/ql/test/query-tests/Security/CWE-760/test.swift +++ b/swift/ql/test/query-tests/Security/CWE-760/test.swift @@ -26,7 +26,7 @@ final class Scrypt { // Helper functions func getConstantString() -> String { - "this string is constant" + "this string is constant" // $ Source } func getConstantArray() -> Array { @@ -40,7 +40,7 @@ func getRandomArray() -> Array { // --- tests --- func test() { - let constantSalt: Array = [0x2a, 0x3a, 0x80, 0x05, 0xaf, 0x46, 0x58, 0x2d, 0x66, 0x52, 0x10, 0xae, 0x86, 0xd3, 0x8e, 0x8f] + let constantSalt: Array = [0x2a, 0x3a, 0x80, 0x05, 0xaf, 0x46, 0x58, 0x2d, 0x66, 0x52, 0x10, 0xae, 0x86, 0xd3, 0x8e, 0x8f] // $ Source let constantStringSalt = getConstantArray() let randomSalt = getRandomArray() let randomArray = getRandomArray() @@ -48,23 +48,23 @@ func test() { let iterations = 120120 // HKDF test cases - let hkdfb1 = HKDF(password: randomArray, salt: constantSalt, info: randomArray, keyLength: 0, variant: variant) // BAD - let hkdfb2 = HKDF(password: randomArray, salt: constantStringSalt, info: randomArray, keyLength: 0, variant: variant) // BAD + let hkdfb1 = HKDF(password: randomArray, salt: constantSalt, info: randomArray, keyLength: 0, variant: variant) // $ Alert + let hkdfb2 = HKDF(password: randomArray, salt: constantStringSalt, info: randomArray, keyLength: 0, variant: variant) // $ Alert let hkdfg1 = HKDF(password: randomArray, salt: randomSalt, info: randomArray, keyLength: 0, variant: variant) // GOOD // PBKDF1 test cases - let pbkdf1b1 = PKCS5.PBKDF1(password: randomArray, salt: constantSalt, iterations: iterations, keyLength: 0) // BAD - let pbkdf1b2 = PKCS5.PBKDF1(password: randomArray, salt: constantStringSalt, iterations: iterations, keyLength: 0) // BAD + let pbkdf1b1 = PKCS5.PBKDF1(password: randomArray, salt: constantSalt, iterations: iterations, keyLength: 0) // $ Alert + let pbkdf1b2 = PKCS5.PBKDF1(password: randomArray, salt: constantStringSalt, iterations: iterations, keyLength: 0) // $ Alert let pbkdf1g1 = PKCS5.PBKDF1(password: randomArray, salt: randomSalt, iterations: iterations, keyLength: 0) // GOOD // PBKDF2 test cases - let pbkdf2b1 = PKCS5.PBKDF2(password: randomArray, salt: constantSalt, iterations: iterations, keyLength: 0) // BAD - let pbkdf2b2 = PKCS5.PBKDF2(password: randomArray, salt: constantStringSalt, iterations: iterations, keyLength: 0) // BAD + let pbkdf2b1 = PKCS5.PBKDF2(password: randomArray, salt: constantSalt, iterations: iterations, keyLength: 0) // $ Alert + let pbkdf2b2 = PKCS5.PBKDF2(password: randomArray, salt: constantStringSalt, iterations: iterations, keyLength: 0) // $ Alert let pbkdf2g1 = PKCS5.PBKDF2(password: randomArray, salt: randomSalt, iterations: iterations, keyLength: 0) // GOOD // Scrypt test cases - let scryptb1 = Scrypt(password: randomArray, salt: constantSalt, dkLen: 64, N: 16384, r: 8, p: 1) // BAD - let scryptb2 = Scrypt(password: randomArray, salt: constantStringSalt, dkLen: 64, N: 16384, r: 8, p: 1) // BAD + let scryptb1 = Scrypt(password: randomArray, salt: constantSalt, dkLen: 64, N: 16384, r: 8, p: 1) // $ Alert + let scryptb2 = Scrypt(password: randomArray, salt: constantStringSalt, dkLen: 64, N: 16384, r: 8, p: 1) // $ Alert let scryptg1 = Scrypt(password: randomArray, salt: randomSalt, dkLen: 64, N: 16384, r: 8, p: 1) // GOOD } diff --git a/swift/ql/test/query-tests/Security/CWE-916/InsufficientHashIterations.qlref b/swift/ql/test/query-tests/Security/CWE-916/InsufficientHashIterations.qlref index 81a6dda0d0f..66492b8441e 100644 --- a/swift/ql/test/query-tests/Security/CWE-916/InsufficientHashIterations.qlref +++ b/swift/ql/test/query-tests/Security/CWE-916/InsufficientHashIterations.qlref @@ -1 +1,2 @@ -queries/Security/CWE-916/InsufficientHashIterations.ql +query: queries/Security/CWE-916/InsufficientHashIterations.ql +postprocess: utils/test/InlineExpectationsTestQuery.ql diff --git a/swift/ql/test/query-tests/Security/CWE-916/test.swift b/swift/ql/test/query-tests/Security/CWE-916/test.swift index 8786d936c1d..5c63fc35265 100644 --- a/swift/ql/test/query-tests/Security/CWE-916/test.swift +++ b/swift/ql/test/query-tests/Security/CWE-916/test.swift @@ -17,7 +17,7 @@ extension PKCS5 { } // Helper functions -func getLowIterationCount() -> Int { return 99999 } +func getLowIterationCount() -> Int { return 99999 } // $ Source func getEnoughIterationCount() -> Int { return 120120 } @@ -34,15 +34,15 @@ func test() { let enoughIterations = getEnoughIterationCount() // PBKDF1 test cases - let pbkdf1b1 = PKCS5.PBKDF1(password: randomArray, salt: randomArray, iterations: lowIterations, keyLength: 0) // BAD - let pbkdf1b2 = PKCS5.PBKDF1(password: randomArray, salt: randomArray, iterations: 80000, keyLength: 0) // BAD + let pbkdf1b1 = PKCS5.PBKDF1(password: randomArray, salt: randomArray, iterations: lowIterations, keyLength: 0) // $ Alert + let pbkdf1b2 = PKCS5.PBKDF1(password: randomArray, salt: randomArray, iterations: 80000, keyLength: 0) // $ Alert let pbkdf1g1 = PKCS5.PBKDF1(password: randomArray, salt: randomArray, iterations: enoughIterations, keyLength: 0) // GOOD let pbkdf1g2 = PKCS5.PBKDF1(password: randomArray, salt: randomArray, iterations: 120120, keyLength: 0) // GOOD // PBKDF2 test cases - let pbkdf2b1 = PKCS5.PBKDF2(password: randomArray, salt: randomArray, iterations: lowIterations, keyLength: 0) // BAD - let pbkdf2b2 = PKCS5.PBKDF2(password: randomArray, salt: randomArray, iterations: 80000, keyLength: 0) // BAD + let pbkdf2b1 = PKCS5.PBKDF2(password: randomArray, salt: randomArray, iterations: lowIterations, keyLength: 0) // $ Alert + let pbkdf2b2 = PKCS5.PBKDF2(password: randomArray, salt: randomArray, iterations: 80000, keyLength: 0) // $ Alert let pbkdf2g1 = PKCS5.PBKDF2(password: randomArray, salt: randomArray, iterations: enoughIterations, keyLength: 0) // GOOD let pbkdf2g2 = PKCS5.PBKDF2(password: randomArray, salt: randomArray, iterations: 120120, keyLength: 0) // GOOD } diff --git a/swift/tools/tracing-config.lua b/swift/tools/tracing-config.lua index a29e7b3b953..22ce71b2e78 100644 --- a/swift/tools/tracing-config.lua +++ b/swift/tools/tracing-config.lua @@ -54,6 +54,8 @@ function RegisterExtractorPack(id) strip_unsupported_arg(args, '-experimental-skip-non-inlinable-function-bodies-without-types', 0) strip_unsupported_clang_arg(args, '-ivfsstatcache', 1) strip_unsupported_clang_arg(args, '-fno-odr-hash-protocols', 0) + strip_unsupported_clang_arg(args, '-fobjc-msgsend-selector-stubs', 0) + strip_unsupported_clang_arg(args, '-fstack-check', 0) strip_unsupported_clang_arg(args, '-clang-vendor-feature=+disableNonDependentMemberExprInCurrentInstantiation', 0) strip_unsupported_clang_arg(args, '-clang-vendor-feature=+enableAggressiveVLAFolding', 0) strip_unsupported_clang_arg(args, '-clang-vendor-feature=+revert09abecef7bbf', 0) diff --git a/unified/ql/lib/codeql/unified/Ast.qll b/unified/ql/lib/codeql/unified/Ast.qll index d9060c26f0f..b6d6a76b549 100644 --- a/unified/ql/lib/codeql/unified/Ast.qll +++ b/unified/ql/lib/codeql/unified/Ast.qll @@ -61,6 +61,18 @@ module Unified { override string getAPrimaryQlClass() { result = "Token" } } + /** A trivia token, such as a comment, preserved from the original parse tree. */ + class TriviaToken extends @unified_trivia_token, AstNode { + /** Gets the source text of this trivia token. */ + final string getValue() { unified_trivia_tokeninfo(this, _, result) } + + /** Gets a string representation of this element. */ + final override string toString() { result = this.getValue() } + + /** Gets the name of the primary QL class for this element. */ + override string getAPrimaryQlClass() { result = "TriviaToken" } + } + /** Gets the file containing the given `node`. */ private @file getNodeFile(@unified_ast_node node) { exists(@location_default loc | unified_ast_node_location(node, loc) | diff --git a/unified/ql/lib/codeql/unified/Comments.qll b/unified/ql/lib/codeql/unified/Comments.qll new file mode 100644 index 00000000000..e839af2dbee --- /dev/null +++ b/unified/ql/lib/codeql/unified/Comments.qll @@ -0,0 +1,18 @@ +/** Provides classes for working with comments. */ + +private import unified + +/** + * A comment appearing in the source code. + */ +class Comment extends TriviaToken { + // At the moment, comments are the only type trivia token we extract + /** + * Gets the text inside this comment, not counting the delimeters. + */ + string getCommentText() { + result = this.getValue().regexpCapture("//(.*)", 1) + or + result = this.getValue().regexpCapture("(?s)/\\*(.*)\\*/", 1) + } +} diff --git a/unified/ql/lib/unified.dbscheme b/unified/ql/lib/unified.dbscheme index 28718d79423..31b3ec6c3ed 100644 --- a/unified/ql/lib/unified.dbscheme +++ b/unified/ql/lib/unified.dbscheme @@ -334,7 +334,13 @@ case @unified_token.kind of ; -@unified_ast_node = @unified_apply_pattern | @unified_binary_expr | @unified_block_stmt | @unified_call_expr | @unified_expr_condition | @unified_expr_stmt | @unified_guard_if_stmt | @unified_if_stmt | @unified_lambda_expr | @unified_let_pattern_condition | @unified_member_access_expr | @unified_name_expr | @unified_parameter | @unified_sequence_condition | @unified_token | @unified_top_level | @unified_tuple_pattern | @unified_unary_expr | @unified_var_pattern | @unified_variable_declaration_stmt | @unified_variable_declarator +unified_trivia_tokeninfo( + unique int id: @unified_trivia_token, + int kind: int ref, + string value: string ref +); + +@unified_ast_node = @unified_apply_pattern | @unified_binary_expr | @unified_block_stmt | @unified_call_expr | @unified_expr_condition | @unified_expr_stmt | @unified_guard_if_stmt | @unified_if_stmt | @unified_lambda_expr | @unified_let_pattern_condition | @unified_member_access_expr | @unified_name_expr | @unified_parameter | @unified_sequence_condition | @unified_token | @unified_top_level | @unified_trivia_token | @unified_tuple_pattern | @unified_unary_expr | @unified_var_pattern | @unified_variable_declaration_stmt | @unified_variable_declarator unified_ast_node_location( unique int node: @unified_ast_node ref, diff --git a/unified/ql/lib/unified.qll b/unified/ql/lib/unified.qll new file mode 100644 index 00000000000..4f7387ef8f1 --- /dev/null +++ b/unified/ql/lib/unified.qll @@ -0,0 +1,8 @@ +/** + * Provides classes for working with the AST, as well as files and locations. + */ + +import codeql.Locations +import codeql.files.FileSystem +import codeql.unified.Ast::Unified +import codeql.unified.Comments diff --git a/unified/ql/test/library-tests/comments/comments.expected b/unified/ql/test/library-tests/comments/comments.expected new file mode 100644 index 00000000000..04e09d06e54 --- /dev/null +++ b/unified/ql/test/library-tests/comments/comments.expected @@ -0,0 +1,3 @@ +| comments.swift:1:1:1:22 | // Hello this is swift | Hello this is swift | +| comments.swift:3:1:6:3 | /*\n * This is a multi-line comment\n * It should be ignored by the parser\n */ | \n * This is a multi-line comment\n * It should be ignored by the parser\n | +| comments.swift:9:5:9:36 | // This is a single-line comment | This is a single-line comment | diff --git a/unified/ql/test/library-tests/comments/comments.ql b/unified/ql/test/library-tests/comments/comments.ql new file mode 100644 index 00000000000..db64ff737a7 --- /dev/null +++ b/unified/ql/test/library-tests/comments/comments.ql @@ -0,0 +1,3 @@ +import unified + +query predicate comments(Comment c, string text) { text = c.getCommentText() } diff --git a/unified/ql/test/library-tests/comments/comments.swift b/unified/ql/test/library-tests/comments/comments.swift new file mode 100644 index 00000000000..9f133142ef2 --- /dev/null +++ b/unified/ql/test/library-tests/comments/comments.swift @@ -0,0 +1,11 @@ +// Hello this is swift + +/* + * This is a multi-line comment + * It should be ignored by the parser + */ + +func hello() { + // This is a single-line comment + print("Hello, world!") +}