diff --git a/PHP/10_return_by_reference/10_return_by_reference.json b/PHP/10_return_by_reference/10_return_by_reference.json old mode 100644 new mode 100755 index 902aedb..c7bc5c7 --- a/PHP/10_return_by_reference/10_return_by_reference.json +++ b/PHP/10_return_by_reference/10_return_by_reference.json @@ -1,14 +1,14 @@ -{ - "name": "Return By Reference", - "description": "[Return by reference](https://www.php.net/manual/en/language.references.return.php) in PHP, when the variable will be referenced to the returned variable from a specific function. Returning by reference is useful when you want to use a function to find to which variable a reference should be bound. If there are more than one return in the function, the variable will be referenced for the first one.", - "family": "code_pattern_php", - "tags": [ - "sast", - "php", - "php_v7.4.9" - ], - "instances": [ - "./1_instance_10_return_by_reference/1_instance_10_return_by_reference.json" - ], - "version": "v0.draft" +{ + "name": "Return By Reference", + "description": "./docs/description.md", + "family": "code_pattern_php", + "tags": [ + "sast", + "php", + "php_v7.4.9" + ], + "instances": [ + "./1_instance_10_return_by_reference/1_instance_10_return_by_reference.json" + ], + "version": "v1.0" } \ No newline at end of file diff --git a/PHP/10_return_by_reference/1_instance_10_return_by_reference/1_instance_10_return_by_reference.bash b/PHP/10_return_by_reference/1_instance_10_return_by_reference/1_instance_10_return_by_reference.bash old mode 100644 new mode 100755 index 96cc266..2654717 --- a/PHP/10_return_by_reference/1_instance_10_return_by_reference/1_instance_10_return_by_reference.bash +++ b/PHP/10_return_by_reference/1_instance_10_return_by_reference/1_instance_10_return_by_reference.bash @@ -1,33 +1,30 @@ - -$_main: ; (lines=17, args=0, vars=3, tmps=7) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/19_return_by_reference/19_return_by_reference.php:1-18 -L0 (3): NOP -L1 (10): EXT_STMT -L2 (10): ASSIGN CV0($b) string("input") -L3 (11): EXT_STMT -L4 (11): V4 = NEW 0 string("foo") -L5 (11): DO_FCALL -L6 (11): ASSIGN CV1($obj) V4 -L7 (13): EXT_STMT -L8 (13): INIT_METHOD_CALL 0 CV1($obj) string("getValue") -L9 (13): V7 = DO_FCALL -L10 (13): ASSIGN_REF (function) CV2($myValue) V7 -L11 (14): EXT_STMT -L12 (14): ASSIGN_OBJ CV1($obj) string("value") -L13 (14): OP_DATA CV0($b) -L14 (17): EXT_STMT -L15 (17): ECHO CV2($myValue) -L16 (18): RETURN int(1) -LIVE RANGES: - 4: L5 - L6 (new) - -foo::getValue: ; (lines=6, args=0, vars=0, tmps=1) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/19_return_by_reference/19_return_by_reference.php:5-7 -L0 (5): EXT_NOP -L1 (6): EXT_STMT -L2 (6): V0 = FETCH_OBJ_W (ref) THIS string("value") -L3 (6): RETURN_BY_REF V0 -L4 (7): EXT_STMT -L5 (7): RETURN_BY_REF (function) null + +$_main: + ; (lines=13, args=0, vars=3, tmps=9) + ; (before optimizer) + ; /.../PHP/10_return_by_reference/1_instance_10_return_by_reference/1_instance_10_return_by_reference.php:1-17 + ; return [] RANGE[0..0] +0000 T3 = FETCH_R (global) string("_GET") +0001 T4 = FETCH_DIM_R T3 string("p1") +0002 ASSIGN CV0($a) T4 +0003 V6 = NEW 0 string("foo") +0004 DO_FCALL +0005 ASSIGN CV1($obj) V6 +0006 INIT_METHOD_CALL 0 CV1($obj) string("getValue") +0007 V9 = DO_FCALL +0008 ASSIGN_REF (function) CV2($b) V9 +0009 ASSIGN_OBJ CV1($obj) string("value") +0010 OP_DATA CV0($a) +0011 ECHO CV2($b) +0012 RETURN int(1) +LIVE RANGES: + 6: 0004 - 0005 (new) + +foo::getValue: + ; (lines=3, args=0, vars=0, tmps=1) + ; (before optimizer) + ; /.../PHP/10_return_by_reference/1_instance_10_return_by_reference/1_instance_10_return_by_reference.php:5-7 + ; return [] RANGE[0..0] +0000 V0 = FETCH_OBJ_W (ref) THIS string("value") +0001 RETURN_BY_REF V0 +0002 RETURN_BY_REF (function) null diff --git a/PHP/10_return_by_reference/1_instance_10_return_by_reference/1_instance_10_return_by_reference.json b/PHP/10_return_by_reference/1_instance_10_return_by_reference/1_instance_10_return_by_reference.json old mode 100644 new mode 100755 index 04dddd0..552e44c --- a/PHP/10_return_by_reference/1_instance_10_return_by_reference/1_instance_10_return_by_reference.json +++ b/PHP/10_return_by_reference/1_instance_10_return_by_reference/1_instance_10_return_by_reference.json @@ -1,37 +1,38 @@ -{ - "code": { - "path": "./1_instance_10_return_by_reference.php", - "injection_skeleton_broken": false - }, - "discovery": { - "rule": "./1_instance_10_return_by_reference.sc", - "method": "joern", - "rule_accuracy": "FP", - "notes": "Question: To be perfect this rule should be combined to search for the assignment of the return by reference function i.e., ASSIGN_REF (function)?" - }, - "compile": { - "binary": "./1_instance_10_return_by_reference.bash", - "instruction": null, - "dependencies": null - }, - "expectation": { - "type": "xss", - "sink_file": "./1_instance_10_return_by_reference.php", - "sink_line": 16, - "source_file": "./1_instance_10_return_by_reference.php", - "source_line": 10, - "expectation": true - }, - "properties": { - "category": "S0", - "feature_vs_internal_api": "FEATURE", - "input_sanitizer": false, - "source_and_sink": false, - "negative_test_case": false - }, - "remediation": { - "notes": "", - "transformation": null, - "modeling_rule": null - } +{ + "description": "This instance target the usage of return values being passed by reference.", + "code": { + "path": "./1_instance_10_return_by_reference.php", + "injection_skeleton_broken": false + }, + "discovery": { + "rule": "./1_instance_10_return_by_reference.sc", + "method": "joern", + "rule_accuracy": "FP", + "notes": "Question: To be perfect this rule should be combined to search for the assignment of the return by reference function i.e., ASSIGN_REF (function)?" + }, + "compile": { + "binary": "./1_instance_10_return_by_reference.bash", + "instruction": null, + "dependencies": null + }, + "expectation": { + "type": "xss", + "sink_file": "./1_instance_10_return_by_reference.php", + "sink_line": 16, + "source_file": "./1_instance_10_return_by_reference.php", + "source_line": 10, + "expectation": true + }, + "properties": { + "category": "S0", + "feature_vs_internal_api": "FEATURE", + "input_sanitizer": false, + "source_and_sink": false, + "negative_test_case": false + }, + "remediation": { + "notes": "", + "transformation": null, + "modeling_rule": null + } } \ No newline at end of file diff --git a/PHP/10_return_by_reference/1_instance_10_return_by_reference/1_instance_10_return_by_reference.php b/PHP/10_return_by_reference/1_instance_10_return_by_reference/1_instance_10_return_by_reference.php old mode 100644 new mode 100755 index 4295fcc..2b721d8 --- a/PHP/10_return_by_reference/1_instance_10_return_by_reference/1_instance_10_return_by_reference.php +++ b/PHP/10_return_by_reference/1_instance_10_return_by_reference/1_instance_10_return_by_reference.php @@ -1,16 +1,16 @@ -value; - } -} - -$a = $_GET["p1"]; -$obj = new foo; -// $b is a reference to $obj->value, which is 42. -$b = &$obj->getValue(); -$obj->value = $a; -// prints the new value of $obj->value, which is the source $a (XSS) -echo $b; +value; + } +} + +$a = $_GET["p1"]; // source +$obj = new foo; +// $b is a reference to $obj->value, which is 42. +$b = &$obj->getValue(); +$obj->value = $a; +// prints the new value of $obj->value, which is the source $a (XSS) +echo $b; // sink diff --git a/PHP/10_return_by_reference/1_instance_10_return_by_reference/1_instance_10_return_by_reference.sc b/PHP/10_return_by_reference/1_instance_10_return_by_reference/1_instance_10_return_by_reference.sc old mode 100644 new mode 100755 index 0219d27..5975b34 --- a/PHP/10_return_by_reference/1_instance_10_return_by_reference/1_instance_10_return_by_reference.sc +++ b/PHP/10_return_by_reference/1_instance_10_return_by_reference/1_instance_10_return_by_reference.sc @@ -1,6 +1,6 @@ -@main def main(name : String): Unit = { - importCpg(name) - val x10 = (name, "10_return_by_reference_iall", cpg.call(".*RETURN_BY_REF.*").argument.code("function").location.toJson); - println(x10) - delete; +@main def main(name : String): Unit = { + importCpg(name) + val x10 = (name, "10_return_by_reference_iall", cpg.call(".*RETURN_BY_REF.*").argument.code("function").location.toJson); + println(x10) + delete; } \ No newline at end of file diff --git a/PHP/10_return_by_reference/README.md b/PHP/10_return_by_reference/README.md old mode 100644 new mode 100755 index 7fd4a55..c8bfe54 --- a/PHP/10_return_by_reference/README.md +++ b/PHP/10_return_by_reference/README.md @@ -1,110 +1,125 @@ -# Pattern: Return by Reference - -## Category - -References - -## Definition - -[Return by reference](https://www.php.net/manual/en/language.references.return.php) in PHP, when the variable will be referenced to the returned variable from a specific function. - ->Returning by reference is useful when you want to use a function to find to which variable a reference should be bound. -> ->If there are more than one return in the function, the variable will be referenced for the first one. - -## Instances - -### Instance 1 - -- CATEGORY: S0 -- FEATURE vs INTERNAL API: FEATURE -- INPUT SANITIZERS: NO -- SOURCES AND SINKS: NO -- NEGATIVE TEST CASES: NO -- CODE: - -```php -value; - } -} - -$b = $_GET["p1"]; -$obj = new foo; -// $myValue is a reference to $obj->value, which is 42. -$myValue = &$obj->getValue(); -$obj->value = $b; -// prints the new value of $obj->value, which is the value of $b. -// Here there is XSS vulnerability. -echo $myValue; -``` - -- MEASUREMENT: - -| Tool | RIPS | phpSAFE | WAP | Progpilot | Comm_1 | Comm_2 | Correct | -| ------------- | ---- | ------- | ---- | --------- | ------- | --------- | ------- | -| Vulnerability | NO | NO | NO | NO | NO | NO | YES | -Measurements Date: 8 June 2021 - -- OPCODE: - -```bash -$_main: ; (lines=17, args=0, vars=3, tmps=7) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/19_return_by_reference/19_return_by_reference.php:1-18 -L0 (3): NOP -L1 (10): EXT_STMT -L2 (10): ASSIGN CV0($b) string("input") -L3 (11): EXT_STMT -L4 (11): V4 = NEW 0 string("foo") -L5 (11): DO_FCALL -L6 (11): ASSIGN CV1($obj) V4 -L7 (13): EXT_STMT -L8 (13): INIT_METHOD_CALL 0 CV1($obj) string("getValue") -L9 (13): V7 = DO_FCALL -L10 (13): ASSIGN_REF (function) CV2($myValue) V7 -L11 (14): EXT_STMT -L12 (14): ASSIGN_OBJ CV1($obj) string("value") -L13 (14): OP_DATA CV0($b) -L14 (17): EXT_STMT -L15 (17): ECHO CV2($myValue) -L16 (18): RETURN int(1) -LIVE RANGES: - 4: L5 - L6 (new) - -foo::getValue: ; (lines=6, args=0, vars=0, tmps=1) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/19_return_by_reference/19_return_by_reference.php:5-7 -L0 (5): EXT_NOP -L1 (6): EXT_STMT -L2 (6): V0 = FETCH_OBJ_W (ref) THIS string("value") -L3 (6): RETURN_BY_REF V0 -L4 (7): EXT_STMT -L5 (7): RETURN_BY_REF (function) null -``` - -- DISCOVERY: - -There is a specific opcode for this type of references called RETURN_BY_REF. - -``` -cpg.call(".*RETURN_BY_REF.*").argument.code("function").location.l -``` - -- PRECONDITIONS: - 1. - -- TRANSFORMATION: - -I show that in the pattern [simple references](https://gitlab.eurecom.fr/alkasar/static-tools---latex/issues/26), we need to have a table of variables with the references between them. In this pattern, I need first to detect the first return in the function, then do the connection between the variable and the returned variable from the function. This pattern show the complexity of following the references for the static tools, especially the variable in the function should be [static](https://gitlab.eurecom.fr/alkasar/static-tools---latex/issues/20), global or object property. In addition to that, we face the same difficulties of call graph. - -``` - -``` - - - +[//]: # (This file is automatically generated. If you wish to make any changes, please use the JSON files and regenerate this file using the tpframework.) + +# Return By Reference + +Tags: sast, php, php_v7.4.9 + +Version: v1.0 + +## Description + +[Return by reference](https://www.php.net/manual/en/language.references.return.php) in PHP, when the variable will be referenced to the returned variable from a specific function. Returning by reference is useful when you want to use a function to find to which variable a reference should be bound. If there are more than one return in the function, the variable will be referenced for the first one. + +## Overview + +| Instances | has discovery rule | discovery method | rule successfull | +|---------------------------|----------------------|--------------------|--------------------| +| [1 Instance](#1-instance) | yes | joern | yes | + +## 1 Instance + +This instance target the usage of return values being passed by reference. + +### Code + +```PHP +value; + } +} + +$a = $_GET["p1"]; // source +$obj = new foo; +// $b is a reference to $obj->value, which is 42. +$b = &$obj->getValue(); +$obj->value = $a; +// prints the new value of $obj->value, which is the source $a (XSS) +echo $b; // sink +``` + +### Instance Properties + +| category | feature_vs_internal_api | input_sanitizer | negative_test_case | source_and_sink | +|------------|---------------------------|-------------------|----------------------|-------------------| +| S0 | FEATURE | no | no | no | + +
+ +More + +
+ + +### Compile + + +```bash +$_main: + ; (lines=13, args=0, vars=3, tmps=9) + ; (before optimizer) + ; /.../PHP/10_return_by_reference/1_instance_10_return_by_reference/1_instance_10_return_by_reference.php:1-17 + ; return [] RANGE[0..0] +0000 T3 = FETCH_R (global) string("_GET") +0001 T4 = FETCH_DIM_R T3 string("p1") +0002 ASSIGN CV0($a) T4 +0003 V6 = NEW 0 string("foo") +0004 DO_FCALL +0005 ASSIGN CV1($obj) V6 +0006 INIT_METHOD_CALL 0 CV1($obj) string("getValue") +0007 V9 = DO_FCALL +0008 ASSIGN_REF (function) CV2($b) V9 +0009 ASSIGN_OBJ CV1($obj) string("value") +0010 OP_DATA CV0($a) +0011 ECHO CV2($b) +0012 RETURN int(1) +LIVE RANGES: + 6: 0004 - 0005 (new) + +foo::getValue: + ; (lines=3, args=0, vars=0, tmps=1) + ; (before optimizer) + ; /.../PHP/10_return_by_reference/1_instance_10_return_by_reference/1_instance_10_return_by_reference.php:5-7 + ; return [] RANGE[0..0] +0000 V0 = FETCH_OBJ_W (ref) THIS string("value") +0001 RETURN_BY_REF V0 +0002 RETURN_BY_REF (function) null +``` + +
+ +
+ + +### Discovery + + +Question: To be perfect this rule should be combined to search for the assignment of the return by reference function i.e., ASSIGN_REF (function)? + +```scala +val x10 = (name, "10_return_by_reference_iall", cpg.call(".*RETURN_BY_REF.*").argument.code("function").location.toJson); +``` + +| discovery method | expected accuracy | +|--------------------|---------------------| +| joern | FP | + +
+ +
+ + +### Measurement + + +| Tool | Comm_1 | Comm_2 | phpSAFE | Progpilot | RIPS | WAP | Ground Truth | +|-------------|----------|----------|-----------|-------------|--------|-------|----------------| +| 08 Jun 2021 | no | no | no | no | no | no | yes | +| 17 May 2023 | no | no | | | | | yes | + +
+ +
diff --git a/PHP/10_return_by_reference/docs/description.md b/PHP/10_return_by_reference/docs/description.md new file mode 100755 index 0000000..4f17c49 --- /dev/null +++ b/PHP/10_return_by_reference/docs/description.md @@ -0,0 +1 @@ +[Return by reference](https://www.php.net/manual/en/language.references.return.php) in PHP, when the variable will be referenced to the returned variable from a specific function. Returning by reference is useful when you want to use a function to find to which variable a reference should be bound. If there are more than one return in the function, the variable will be referenced for the first one. \ No newline at end of file diff --git a/PHP/11_foreach_with_reference/11_foreach_with_reference.json b/PHP/11_foreach_with_reference/11_foreach_with_reference.json old mode 100644 new mode 100755 index 0b559ff..ba33d8f --- a/PHP/11_foreach_with_reference/11_foreach_with_reference.json +++ b/PHP/11_foreach_with_reference/11_foreach_with_reference.json @@ -1,14 +1,14 @@ -{ - "name": "ForEach with Reference", - "description": "Foreach is used by high level programming languages to iterate over arrays and objects.\n```php\n + +More + +
+ + +### Compile + + +```bash +$_main: + ; (lines=12, args=0, vars=3, tmps=7) + ; (before optimizer) + ; /.../PHP/11_foreach_with_reference/1_instance_11_foreach_with_reference/1_instance_11_foreach_with_reference.php:1-11 + ; return [] RANGE[0..0] +0000 ASSIGN CV0($arr) array(...) +0001 T4 = FETCH_R (global) string("_GET") +0002 T5 = FETCH_DIM_R T4 string("p1") +0003 ASSIGN CV1($a) T5 +0004 V7 = FE_RESET_RW CV0($arr) 0008 +0005 FE_FETCH_RW V7 CV2($x) 0008 +0006 ASSIGN CV2($x) CV1($a) +0007 JMP 0005 +0008 FE_FREE V7 +0009 T9 = FETCH_DIM_R CV0($arr) int(0) +0010 ECHO T9 +0011 RETURN int(1) +LIVE RANGES: + 7: 0005 - 0008 (loop) +``` + +
+ +
+ + +### Discovery + + +```scala +val x11 = (name, "11_foreach_with_reference_iall", cpg.call(".*FE_FETCH_RW.*").location.toJson); +``` + +| discovery method | expected accuracy | +|--------------------|---------------------| +| joern | Perfect | + +
+ +
+ + +### Measurement + + +| Tool | Comm_1 | Comm_2 | phpSAFE | Progpilot | RIPS | WAP | Ground Truth | +|-------------|----------|----------|-----------|-------------|--------|-------|----------------| +| 08 Jun 2021 | no | no | no | no | no | no | yes | +| 17 May 2023 | no | yes | | | | | yes | + +
+ +
+ + +### Remediation + + +If possible, a copy of the array could be created and it could than be looped over the copy of that array. After the loop, the copy could be assigned back to the original array. However, is highly dependent on the content of the loop and can therefor only be done manually. + +
+ + diff --git a/PHP/11_foreach_with_reference/docs/description.md b/PHP/11_foreach_with_reference/docs/description.md new file mode 100755 index 0000000..1300ead --- /dev/null +++ b/PHP/11_foreach_with_reference/docs/description.md @@ -0,0 +1,19 @@ +Foreach is used by high level programming languages to iterate over arrays and objects. + +```php + + + +## 1 Instance + + +This instance shows, that the content of two different array elements can be connected using a reference. If one changes, the other one changes as well. + +### Code + +```PHP + + +More + +
+ + +### Compile + + +```bash +$_main: + ; (lines=12, args=0, vars=1, tmps=9) + ; (before optimizer) + ; /.../PHP/12_make_ref/1_instance_12_make_ref/1_instance_12_make_ref.php:1-5 + ; return [] RANGE[0..0] +0000 ASSIGN CV0($arr) array(...) +0001 V3 = FETCH_DIM_W CV0($arr) int(3) +0002 V4 = MAKE_REF V3 +0003 V2 = FETCH_DIM_W CV0($arr) int(1) +0004 ASSIGN_REF V2 V4 +0005 T7 = FETCH_R (global) string("_GET") +0006 T8 = FETCH_DIM_R T7 string("p1") +0007 ASSIGN_DIM CV0($arr) int(3) +0008 OP_DATA T8 +0009 T9 = FETCH_DIM_R CV0($arr) int(1) +0010 ECHO T9 +0011 RETURN int(1) +LIVE RANGES: + 4: 0003 - 0004 (tmp/var) +``` + +
+ +
+ + +### Discovery + + +The rule searches for occurences of `MAKE_REF` and should therefor be perfect. + +```scala +val x12 = (name, "12_make_ref_iall_i1", cpg.call(".*MAKE_REF.*").location.toJson); +``` + +| discovery method | expected accuracy | +|--------------------|---------------------| +| joern | Perfect | + +
+ +
+ + +### Measurement + + +| Tool | Comm_1 | Comm_2 | Progpilot | Ground Truth | +|-------------|----------|----------|-------------|----------------| +| 08 Jun 2021 | | yes | no | no | +| 17 May 2023 | no | no | | no | + +
+ + + + + +
+ + +## 2 Instance + + +This instance does not have a vulnerability. It shows, that only the two referenced values are actually connected. If you output a different value, this is user controlled value. + +### Code + +```PHP + + +More + +
+ + +### Compile + + +```bash +$_main: + ; (lines=12, args=0, vars=1, tmps=9) + ; (before optimizer) + ; /.../PHP/12_make_ref/2_instance_12_make_ref/2_instance_12_make_ref.php:1-5 + ; return [] RANGE[0..0] +0000 ASSIGN CV0($arr) array(...) +0001 T3 = FETCH_R (global) string("_GET") +0002 T4 = FETCH_DIM_R T3 string("p1") +0003 ASSIGN_DIM CV0($arr) int(3) +0004 OP_DATA T4 +0005 V6 = FETCH_DIM_W CV0($arr) int(3) +0006 V7 = MAKE_REF V6 +0007 V5 = FETCH_DIM_W CV0($arr) int(1) +0008 ASSIGN_REF V5 V7 +0009 T9 = FETCH_DIM_R CV0($arr) int(0) +0010 ECHO T9 +0011 RETURN int(1) +LIVE RANGES: + 7: 0007 - 0008 (tmp/var) +``` + +
+ +
+ + +### Discovery + + +The rule searches for occurences of `MAKE_REF` and should therefor be perfect. + +```scala +val x12 = (name, "12_make_ref_iall_i1", cpg.call(".*MAKE_REF.*").location.toJson); +``` + +| discovery method | expected accuracy | +|--------------------|---------------------| +| joern | Perfect | + +
+ +
+ + +### Measurement + + +| Tool | Comm_1 | Comm_2 | phpSAFE | Progpilot | RIPS | WAP | Ground Truth | +|-------------|----------|----------|-----------|-------------|--------|-------|----------------| +| 08 Jun 2021 | no | yes | no | yes | no | no | no | +| 17 May 2023 | no | no | | | | | no | + +
+ +
+ + diff --git a/PHP/12_make_ref/docs/description.md b/PHP/12_make_ref/docs/description.md new file mode 100755 index 0000000..c66dff1 --- /dev/null +++ b/PHP/12_make_ref/docs/description.md @@ -0,0 +1 @@ +Multiple variables can point to the same object in memory using references. When the value of one variable changes, it changes the value of all the other variables referencing that same value. \ No newline at end of file diff --git a/PHP/13_assign_static_prop_ref/13_assign_static_prop_ref.json b/PHP/13_assign_static_prop_ref/13_assign_static_prop_ref.json old mode 100644 new mode 100755 index b1f69b0..3d47d8a --- a/PHP/13_assign_static_prop_ref/13_assign_static_prop_ref.json +++ b/PHP/13_assign_static_prop_ref/13_assign_static_prop_ref.json @@ -1,14 +1,14 @@ -{ - "name": "Assign Static Prop Ref", - "description": "", - "family": "code_pattern_php", - "tags": [ - "sast", - "php", - "php_v7.4.9" - ], - "instances": [ - "./1_instance_13_assign_static_prop_ref/1_instance_13_assign_static_prop_ref.json" - ], - "version": "v0.draft" +{ + "name": "Assign Static Prop Ref", + "description": "./docs/description.md", + "family": "code_pattern_php", + "tags": [ + "sast", + "php", + "php_v7.4.9" + ], + "instances": [ + "./1_instance_13_assign_static_prop_ref/1_instance_13_assign_static_prop_ref.json" + ], + "version": "v1.0" } \ No newline at end of file diff --git a/PHP/13_assign_static_prop_ref/1_instance_13_assign_static_prop_ref/1_instance_13_assign_static_prop_ref.bash b/PHP/13_assign_static_prop_ref/1_instance_13_assign_static_prop_ref/1_instance_13_assign_static_prop_ref.bash old mode 100644 new mode 100755 index 443b794..e45d5af --- a/PHP/13_assign_static_prop_ref/1_instance_13_assign_static_prop_ref/1_instance_13_assign_static_prop_ref.bash +++ b/PHP/13_assign_static_prop_ref/1_instance_13_assign_static_prop_ref/1_instance_13_assign_static_prop_ref.bash @@ -1,18 +1,15 @@ - -$_main: ; (lines=14, args=0, vars=1, tmps=6) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/122_assign_static_prop_ref/122_assign_static_prop_ref.php:1-10 -L0 (3): NOP -L1 (6): EXT_STMT -L2 (6): ASSIGN CV0($y) string("abc") -L3 (7): EXT_STMT -L4 (7): ASSIGN_STATIC_PROP_REF string("sprop") string("myclass") -L5 (7): OP_DATA CV0($y) -L6 (8): EXT_STMT -L7 (8): T3 = FETCH_R (global) string("_GET") -L8 (8): T4 = FETCH_DIM_R T3 string("p1") -L9 (8): ASSIGN CV0($y) T4 -L10 (9): EXT_STMT -L11 (9): T6 = FETCH_STATIC_PROP_R string("sprop") string("myclass") -L12 (9): ECHO T6 -L13 (10): RETURN int(1) + +$_main: + ; (lines=9, args=0, vars=1, tmps=6) + ; (before optimizer) + ; /.../PHP/13_assign_static_prop_ref/1_instance_13_assign_static_prop_ref/1_instance_13_assign_static_prop_ref.php:1-9 + ; return [] RANGE[0..0] +0000 ASSIGN CV0($y) string("abc") +0001 ASSIGN_STATIC_PROP_REF string("sprop") string("myclass") +0002 OP_DATA CV0($y) +0003 T3 = FETCH_R (global) string("_GET") +0004 T4 = FETCH_DIM_R T3 string("p1") +0005 ASSIGN CV0($y) T4 +0006 T6 = FETCH_STATIC_PROP_R string("sprop") string("myclass") +0007 ECHO T6 +0008 RETURN int(1) diff --git a/PHP/13_assign_static_prop_ref/1_instance_13_assign_static_prop_ref/1_instance_13_assign_static_prop_ref.json b/PHP/13_assign_static_prop_ref/1_instance_13_assign_static_prop_ref/1_instance_13_assign_static_prop_ref.json old mode 100644 new mode 100755 index c650e59..0b10176 --- a/PHP/13_assign_static_prop_ref/1_instance_13_assign_static_prop_ref/1_instance_13_assign_static_prop_ref.json +++ b/PHP/13_assign_static_prop_ref/1_instance_13_assign_static_prop_ref/1_instance_13_assign_static_prop_ref.json @@ -1,37 +1,38 @@ -{ - "code": { - "path": "./1_instance_13_assign_static_prop_ref.php", - "injection_skeleton_broken": false - }, - "discovery": { - "rule": "./1_instance_13_assign_static_prop_ref.sc", - "method": "joern", - "rule_accuracy": "Perfect", - "notes": null - }, - "compile": { - "binary": "./1_instance_13_assign_static_prop_ref.bash", - "instruction": null, - "dependencies": null - }, - "expectation": { - "type": "xss", - "sink_file": "./1_instance_13_assign_static_prop_ref.php", - "sink_line": 9, - "source_file": "./1_instance_13_assign_static_prop_ref.php", - "source_line": 8, - "expectation": true - }, - "properties": { - "category": "S0", - "feature_vs_internal_api": "FEATURE", - "input_sanitizer": false, - "source_and_sink": false, - "negative_test_case": false - }, - "remediation": { - "notes": "", - "transformation": null, - "modeling_rule": null - } +{ + "description": "The instance shows the assignement by reference to a static class variable.", + "code": { + "path": "./1_instance_13_assign_static_prop_ref.php", + "injection_skeleton_broken": false + }, + "discovery": { + "rule": "./1_instance_13_assign_static_prop_ref.sc", + "method": "joern", + "rule_accuracy": "Perfect", + "notes": "The rule is searching for `ASSIGN_STATIC_PROP_REF`, which assignes a value by reference to a static property." + }, + "compile": { + "binary": "./1_instance_13_assign_static_prop_ref.bash", + "instruction": null, + "dependencies": null + }, + "expectation": { + "type": "xss", + "sink_file": "./1_instance_13_assign_static_prop_ref.php", + "sink_line": 9, + "source_file": "./1_instance_13_assign_static_prop_ref.php", + "source_line": 8, + "expectation": true + }, + "properties": { + "category": "S0", + "feature_vs_internal_api": "FEATURE", + "input_sanitizer": false, + "source_and_sink": false, + "negative_test_case": false + }, + "remediation": { + "notes": "", + "transformation": null, + "modeling_rule": null + } } \ No newline at end of file diff --git a/PHP/13_assign_static_prop_ref/1_instance_13_assign_static_prop_ref/1_instance_13_assign_static_prop_ref.php b/PHP/13_assign_static_prop_ref/1_instance_13_assign_static_prop_ref/1_instance_13_assign_static_prop_ref.php old mode 100644 new mode 100755 index 99811ae..e949cb0 --- a/PHP/13_assign_static_prop_ref/1_instance_13_assign_static_prop_ref/1_instance_13_assign_static_prop_ref.php +++ b/PHP/13_assign_static_prop_ref/1_instance_13_assign_static_prop_ref/1_instance_13_assign_static_prop_ref.php @@ -1,9 +1,9 @@ - + +More + +
+ + +### Compile + + +```bash +$_main: + ; (lines=9, args=0, vars=1, tmps=6) + ; (before optimizer) + ; /.../PHP/13_assign_static_prop_ref/1_instance_13_assign_static_prop_ref/1_instance_13_assign_static_prop_ref.php:1-9 + ; return [] RANGE[0..0] +0000 ASSIGN CV0($y) string("abc") +0001 ASSIGN_STATIC_PROP_REF string("sprop") string("myclass") +0002 OP_DATA CV0($y) +0003 T3 = FETCH_R (global) string("_GET") +0004 T4 = FETCH_DIM_R T3 string("p1") +0005 ASSIGN CV0($y) T4 +0006 T6 = FETCH_STATIC_PROP_R string("sprop") string("myclass") +0007 ECHO T6 +0008 RETURN int(1) +``` + +
+ +
+ + +### Discovery + + +The rule is searching for `ASSIGN_STATIC_PROP_REF`, which assignes a value by reference to a static property. + +```scala +val x13 = (name, "13_assign_static_prop_ref_iall", cpg.call(".*ASSIGN_STATIC_PROP_REF.*").location.toJson); +``` + +| discovery method | expected accuracy | +|--------------------|---------------------| +| joern | Perfect | + +
+ +
+ + +### Measurement + + +| Tool | Comm_1 | Comm_2 | phpSAFE | Progpilot | RIPS | WAP | Ground Truth | +|-------------|----------|----------|-----------|-------------|--------|-------|----------------| +| 08 Jun 2021 | yes | no | no | yes | no | no | yes | +| 17 May 2023 | yes | no | | | | | yes | + +
+ + diff --git a/PHP/13_assign_static_prop_ref/docs/description.md b/PHP/13_assign_static_prop_ref/docs/description.md new file mode 100755 index 0000000..157a6df --- /dev/null +++ b/PHP/13_assign_static_prop_ref/docs/description.md @@ -0,0 +1 @@ +A static variable in a class in PHP is associated with that class rather than an instance of the class. \ No newline at end of file diff --git a/PHP/14_object_assigned_by_reference/14_object_assigned_by_reference.json b/PHP/14_object_assigned_by_reference/14_object_assigned_by_reference.json old mode 100644 new mode 100755 index 7bd9b3d..4fe6a5b --- a/PHP/14_object_assigned_by_reference/14_object_assigned_by_reference.json +++ b/PHP/14_object_assigned_by_reference/14_object_assigned_by_reference.json @@ -1,16 +1,16 @@ -{ - "name": "Object Assigned By Reference", - "description": "", - "family": "code_pattern_php", - "tags": [ - "sast", - "php", - "php_v7.4.9" - ], - "instances": [ - "./1_instance_14_object_assigned_by_reference/1_instance_14_object_assigned_by_reference.json", - "./2_instance_14_object_assigned_by_reference/2_instance_14_object_assigned_by_reference.json", - "./3_instance_14_object_assigned_by_reference/3_instance_14_object_assigned_by_reference.json" - ], - "version": "v0.draft" +{ + "name": "Object Assigned By Reference", + "description": "./docs/description.md", + "family": "code_pattern_php", + "tags": [ + "sast", + "php", + "php_v7.4.9" + ], + "instances": [ + "./1_instance_14_object_assigned_by_reference/1_instance_14_object_assigned_by_reference.json", + "./2_instance_14_object_assigned_by_reference/2_instance_14_object_assigned_by_reference.json", + "./3_instance_14_object_assigned_by_reference/3_instance_14_object_assigned_by_reference.json" + ], + "version": "v1.0" } \ No newline at end of file diff --git a/PHP/14_object_assigned_by_reference/1_instance_14_object_assigned_by_reference/1_instance_14_object_assigned_by_reference.sc b/PHP/14_object_assigned_by_reference/14_object_assigned_by_reference.sc old mode 100644 new mode 100755 similarity index 97% rename from PHP/14_object_assigned_by_reference/1_instance_14_object_assigned_by_reference/1_instance_14_object_assigned_by_reference.sc rename to PHP/14_object_assigned_by_reference/14_object_assigned_by_reference.sc index 6cf44f7..9596b2d --- a/PHP/14_object_assigned_by_reference/1_instance_14_object_assigned_by_reference/1_instance_14_object_assigned_by_reference.sc +++ b/PHP/14_object_assigned_by_reference/14_object_assigned_by_reference.sc @@ -1,6 +1,6 @@ -@main def main(name : String): Unit = { - importCpg(name) - val x14 = (name, "14_object_assigned_by_reference_iall", cpg.call(".*ASSIGN_OBJ_REF.*").location.toJson); - println(x14) - delete; +@main def main(name : String): Unit = { + importCpg(name) + val x14 = (name, "14_object_assigned_by_reference_iall", cpg.call(".*ASSIGN_OBJ_REF.*").location.toJson); + println(x14) + delete; } \ No newline at end of file diff --git a/PHP/14_object_assigned_by_reference/1_instance_14_object_assigned_by_reference/1_instance_14_object_assigned_by_reference.bash b/PHP/14_object_assigned_by_reference/1_instance_14_object_assigned_by_reference/1_instance_14_object_assigned_by_reference.bash old mode 100644 new mode 100755 index 7ef0e65..5fb33c1 --- a/PHP/14_object_assigned_by_reference/1_instance_14_object_assigned_by_reference/1_instance_14_object_assigned_by_reference.bash +++ b/PHP/14_object_assigned_by_reference/1_instance_14_object_assigned_by_reference/1_instance_14_object_assigned_by_reference.bash @@ -1,20 +1,20 @@ - -$_main: ; (lines=16, args=0, vars=2, tmps=7) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/121_object_assigned_by_reference/121_object_assigned_by_reference.php:1-9 -L0 (3): NOP -L1 (5): EXT_STMT -L2 (5): ASSIGN CV0($x) string("safe") -L3 (6): EXT_STMT -L4 (6): ASSIGN CV1($obj) string("abc") -L5 (7): EXT_STMT -L6 (7): ASSIGN_OBJ_REF CV1($obj) string("prop") -L7 (7): OP_DATA CV0($x) -L8 (8): EXT_STMT -L9 (8): T5 = FETCH_R (global) string("_GET") -L10 (8): T6 = FETCH_DIM_R T5 string("p1") -L11 (8): ASSIGN CV0($x) T6 -L12 (9): EXT_STMT -L13 (9): T8 = FETCH_OBJ_R CV1($obj) string("prop") -L14 (9): ECHO T8 -L15 (9): RETURN int(1) + +$_main: + ; (lines=12, args=0, vars=2, tmps=9) + ; (before optimizer) + ; /.../PHP/14_object_assigned_by_reference/1_instance_14_object_assigned_by_reference/1_instance_14_object_assigned_by_reference.php:1-9 + ; return [] RANGE[0..0] +0000 ASSIGN CV0($x) string("safe") +0001 V3 = NEW 0 string("myclass") +0002 DO_FCALL +0003 ASSIGN CV1($obj) V3 +0004 ASSIGN_OBJ_REF CV1($obj) string("prop") +0005 OP_DATA CV0($x) +0006 T7 = FETCH_R (global) string("_GET") +0007 T8 = FETCH_DIM_R T7 string("p1") +0008 ASSIGN CV0($x) T8 +0009 T10 = FETCH_OBJ_R CV1($obj) string("prop") +0010 ECHO T10 +0011 RETURN int(1) +LIVE RANGES: + 3: 0002 - 0003 (new) diff --git a/PHP/14_object_assigned_by_reference/1_instance_14_object_assigned_by_reference/1_instance_14_object_assigned_by_reference.json b/PHP/14_object_assigned_by_reference/1_instance_14_object_assigned_by_reference/1_instance_14_object_assigned_by_reference.json old mode 100644 new mode 100755 index 0189b0c..c27ac3e --- a/PHP/14_object_assigned_by_reference/1_instance_14_object_assigned_by_reference/1_instance_14_object_assigned_by_reference.json +++ b/PHP/14_object_assigned_by_reference/1_instance_14_object_assigned_by_reference/1_instance_14_object_assigned_by_reference.json @@ -1,37 +1,38 @@ -{ - "code": { - "path": "./1_instance_14_object_assigned_by_reference.php", - "injection_skeleton_broken": true - }, - "discovery": { - "rule": "./1_instance_14_object_assigned_by_reference.sc", - "method": "joern", - "rule_accuracy": "Perfect", - "notes": null - }, - "compile": { - "binary": "./1_instance_14_object_assigned_by_reference.bash", - "instruction": null, - "dependencies": null - }, - "expectation": { - "type": "xss", - "sink_file": "./1_instance_14_object_assigned_by_reference.php", - "sink_line": 9, - "source_file": "./1_instance_14_object_assigned_by_reference.php", - "source_line": 8, - "expectation": true - }, - "properties": { - "category": "S0", - "feature_vs_internal_api": "FEATURE", - "input_sanitizer": false, - "source_and_sink": false, - "negative_test_case": false - }, - "remediation": { - "notes": "", - "transformation": null, - "modeling_rule": null - } +{ + "description": "The instance shows a variable assigned to an instance variable of an object by reference. The instance variable is first assigned to the other variable and than the other variable is set to a user controlled input.", + "code": { + "path": "./1_instance_14_object_assigned_by_reference.php", + "injection_skeleton_broken": true + }, + "discovery": { + "rule": "../14_object_assigned_by_reference.sc", + "method": "joern", + "rule_accuracy": "Perfect", + "notes": "../docs/discovery_notes.md" + }, + "compile": { + "binary": "./1_instance_14_object_assigned_by_reference.bash", + "instruction": null, + "dependencies": null + }, + "expectation": { + "type": "xss", + "sink_file": "./1_instance_14_object_assigned_by_reference.php", + "sink_line": 9, + "source_file": "./1_instance_14_object_assigned_by_reference.php", + "source_line": 8, + "expectation": true + }, + "properties": { + "category": "S0", + "feature_vs_internal_api": "FEATURE", + "input_sanitizer": false, + "source_and_sink": false, + "negative_test_case": false + }, + "remediation": { + "notes": "", + "transformation": null, + "modeling_rule": null + } } \ No newline at end of file diff --git a/PHP/14_object_assigned_by_reference/1_instance_14_object_assigned_by_reference/1_instance_14_object_assigned_by_reference.php b/PHP/14_object_assigned_by_reference/1_instance_14_object_assigned_by_reference/1_instance_14_object_assigned_by_reference.php old mode 100644 new mode 100755 index cd3e3dd..32a24e0 --- a/PHP/14_object_assigned_by_reference/1_instance_14_object_assigned_by_reference/1_instance_14_object_assigned_by_reference.php +++ b/PHP/14_object_assigned_by_reference/1_instance_14_object_assigned_by_reference/1_instance_14_object_assigned_by_reference.php @@ -1,9 +1,9 @@ -prop = &$x; // tarpit -$x = $_GET["p1"]; // source +prop = &$x; // tarpit +$x = $_GET["p1"]; // source echo $obj->prop; // sink \ No newline at end of file diff --git a/PHP/14_object_assigned_by_reference/2_instance_14_object_assigned_by_reference/2_instance_14_object_assigned_by_reference.bash b/PHP/14_object_assigned_by_reference/2_instance_14_object_assigned_by_reference/2_instance_14_object_assigned_by_reference.bash new file mode 100755 index 0000000..b97705b --- /dev/null +++ b/PHP/14_object_assigned_by_reference/2_instance_14_object_assigned_by_reference/2_instance_14_object_assigned_by_reference.bash @@ -0,0 +1,20 @@ + +$_main: + ; (lines=12, args=0, vars=2, tmps=9) + ; (before optimizer) + ; /.../PHP/14_object_assigned_by_reference/2_instance_14_object_assigned_by_reference/2_instance_14_object_assigned_by_reference.php:1-9 + ; return [] RANGE[0..0] +0000 ASSIGN CV0($x) string("safe") +0001 V3 = NEW 0 string("myclass") +0002 DO_FCALL +0003 ASSIGN CV1($obj) V3 +0004 T6 = FETCH_R (global) string("_GET") +0005 T7 = FETCH_DIM_R T6 string("p1") +0006 ASSIGN CV0($x) T7 +0007 ASSIGN_OBJ_REF CV1($obj) string("prop") +0008 OP_DATA CV0($x) +0009 T10 = FETCH_OBJ_R CV1($obj) string("prop") +0010 ECHO T10 +0011 RETURN int(1) +LIVE RANGES: + 3: 0002 - 0003 (new) diff --git a/PHP/14_object_assigned_by_reference/2_instance_14_object_assigned_by_reference/2_instance_14_object_assigned_by_reference.json b/PHP/14_object_assigned_by_reference/2_instance_14_object_assigned_by_reference/2_instance_14_object_assigned_by_reference.json old mode 100644 new mode 100755 index 27fcff0..6e183ba --- a/PHP/14_object_assigned_by_reference/2_instance_14_object_assigned_by_reference/2_instance_14_object_assigned_by_reference.json +++ b/PHP/14_object_assigned_by_reference/2_instance_14_object_assigned_by_reference/2_instance_14_object_assigned_by_reference.json @@ -1,37 +1,38 @@ -{ - "code": { - "path": "./2_instance__14_object_assigned_by_reference.php", - "injection_skeleton_broken": false - }, - "discovery": { - "rule": "./../1_instance__14_object_assigned_by_reference/1_instance__14_object_assigned_by_reference.sc", - "method": "joern", - "rule_accuracy": "Perfect", - "notes": null - }, - "compile": { - "binary": null, - "instruction": null, - "dependencies": null - }, - "expectation": { - "type": "xss", - "sink_file": "./2_instance__14_object_assigned_by_reference.php", - "sink_line": 9, - "source_file": "./2_instance__14_object_assigned_by_reference.php", - "source_line": 7, - "expectation": true - }, - "properties": { - "category": "S0", - "feature_vs_internal_api": "FEATURE", - "input_sanitizer": false, - "source_and_sink": false, - "negative_test_case": false - }, - "remediation": { - "notes": "", - "transformation": null, - "modeling_rule": null - } +{ + "description": "The instance shows a variable assigned to an instance variable of an object by reference. The other variable is first set to a user controlled input and than this variable is assigned to an instance variable of an object.", + "code": { + "path": "./2_instance_14_object_assigned_by_reference.php", + "injection_skeleton_broken": false + }, + "discovery": { + "rule": "../14_object_assigned_by_reference.sc", + "method": "joern", + "rule_accuracy": "Perfect", + "notes": "../docs/discovery_notes.md" + }, + "compile": { + "binary": "./2_instance_14_object_assigned_by_reference.bash", + "instruction": null, + "dependencies": null + }, + "expectation": { + "type": "xss", + "sink_file": "./2_instance_14_object_assigned_by_reference.php", + "sink_line": 9, + "source_file": "./2_instance_14_object_assigned_by_reference.php", + "source_line": 7, + "expectation": true + }, + "properties": { + "category": "S0", + "feature_vs_internal_api": "FEATURE", + "input_sanitizer": false, + "source_and_sink": false, + "negative_test_case": false + }, + "remediation": { + "notes": "", + "transformation": null, + "modeling_rule": null + } } \ No newline at end of file diff --git a/PHP/14_object_assigned_by_reference/2_instance_14_object_assigned_by_reference/2_instance_14_object_assigned_by_reference.php b/PHP/14_object_assigned_by_reference/2_instance_14_object_assigned_by_reference/2_instance_14_object_assigned_by_reference.php old mode 100644 new mode 100755 index ef20f68..f6a936b --- a/PHP/14_object_assigned_by_reference/2_instance_14_object_assigned_by_reference/2_instance_14_object_assigned_by_reference.php +++ b/PHP/14_object_assigned_by_reference/2_instance_14_object_assigned_by_reference/2_instance_14_object_assigned_by_reference.php @@ -1,9 +1,9 @@ -prop = &$x; // tarpit +prop = &$x; // tarpit echo $obj->prop; // sink \ No newline at end of file diff --git a/PHP/14_object_assigned_by_reference/3_instance_14_object_assigned_by_reference/3_instance_14_object_assigned_by_reference.bash b/PHP/14_object_assigned_by_reference/3_instance_14_object_assigned_by_reference/3_instance_14_object_assigned_by_reference.bash new file mode 100755 index 0000000..b77974e --- /dev/null +++ b/PHP/14_object_assigned_by_reference/3_instance_14_object_assigned_by_reference/3_instance_14_object_assigned_by_reference.bash @@ -0,0 +1,21 @@ + +$_main: + ; (lines=13, args=0, vars=3, tmps=10) + ; (before optimizer) + ; /.../PHP/14_object_assigned_by_reference/3_instance_14_object_assigned_by_reference/3_instance_14_object_assigned_by_reference.php:1-10 + ; return [] RANGE[0..0] +0000 T3 = FETCH_R (global) string("_GET") +0001 T4 = FETCH_DIM_R T3 string("p1") +0002 ASSIGN CV0($a) T4 +0003 ASSIGN CV1($x) string("safe") +0004 V7 = NEW 0 string("myclass") +0005 DO_FCALL +0006 ASSIGN CV2($obj) V7 +0007 ASSIGN_OBJ_REF CV2($obj) string("prop") +0008 OP_DATA CV1($x) +0009 ASSIGN CV1($x) CV0($a) +0010 T12 = FETCH_OBJ_R CV2($obj) string("prop") +0011 ECHO T12 +0012 RETURN int(1) +LIVE RANGES: + 7: 0005 - 0006 (new) diff --git a/PHP/14_object_assigned_by_reference/3_instance_14_object_assigned_by_reference/3_instance_14_object_assigned_by_reference.json b/PHP/14_object_assigned_by_reference/3_instance_14_object_assigned_by_reference/3_instance_14_object_assigned_by_reference.json old mode 100644 new mode 100755 index fb345d2..c8a64ad --- a/PHP/14_object_assigned_by_reference/3_instance_14_object_assigned_by_reference/3_instance_14_object_assigned_by_reference.json +++ b/PHP/14_object_assigned_by_reference/3_instance_14_object_assigned_by_reference/3_instance_14_object_assigned_by_reference.json @@ -1,37 +1,38 @@ -{ - "code": { - "path": "./3_instance___14_object_assigned_by_reference.php", - "injection_skeleton_broken": false - }, - "discovery": { - "rule": "./../1_instance___14_object_assigned_by_reference/1_instance___14_object_assigned_by_reference.sc", - "method": "joern", - "rule_accuracy": "Perfect", - "notes": null - }, - "compile": { - "binary": null, - "instruction": null, - "dependencies": null - }, - "expectation": { - "type": "xss", - "sink_file": "./3_instance___14_object_assigned_by_reference.php", - "sink_line": 10, - "source_file": "./3_instance___14_object_assigned_by_reference.php", - "source_line": 5, - "expectation": true - }, - "properties": { - "category": "S0", - "feature_vs_internal_api": "FEATURE", - "input_sanitizer": false, - "source_and_sink": false, - "negative_test_case": false - }, - "remediation": { - "notes": "", - "transformation": null, - "modeling_rule": null - } +{ + "description": "This instance provides another indirection through which the user input reaches the sink. In this instance, it is stored in an intermediate variable, which is assigned to another variable, which itself links to the instance variable, that is used for the output.", + "code": { + "path": "./3_instance_14_object_assigned_by_reference.php", + "injection_skeleton_broken": false + }, + "discovery": { + "rule": "../14_object_assigned_by_reference.sc", + "method": "joern", + "rule_accuracy": "Perfect", + "notes": "../docs/discovery_notes.md" + }, + "compile": { + "binary": "./3_instance_14_object_assigned_by_reference.bash", + "instruction": null, + "dependencies": null + }, + "expectation": { + "type": "xss", + "sink_file": "./3_instance_14_object_assigned_by_reference.php", + "sink_line": 10, + "source_file": "./3_instance_14_object_assigned_by_reference.php", + "source_line": 5, + "expectation": true + }, + "properties": { + "category": "S0", + "feature_vs_internal_api": "FEATURE", + "input_sanitizer": false, + "source_and_sink": false, + "negative_test_case": false + }, + "remediation": { + "notes": "", + "transformation": null, + "modeling_rule": null + } } \ No newline at end of file diff --git a/PHP/14_object_assigned_by_reference/3_instance_14_object_assigned_by_reference/3_instance_14_object_assigned_by_reference.php b/PHP/14_object_assigned_by_reference/3_instance_14_object_assigned_by_reference/3_instance_14_object_assigned_by_reference.php old mode 100644 new mode 100755 index d14a60a..01d5b0a --- a/PHP/14_object_assigned_by_reference/3_instance_14_object_assigned_by_reference/3_instance_14_object_assigned_by_reference.php +++ b/PHP/14_object_assigned_by_reference/3_instance_14_object_assigned_by_reference/3_instance_14_object_assigned_by_reference.php @@ -1,10 +1,10 @@ -prop = &$x; // tarpit -$x = $a; +prop = &$x; // tarpit +$x = $a; echo $obj->prop; // sink \ No newline at end of file diff --git a/PHP/14_object_assigned_by_reference/README.md b/PHP/14_object_assigned_by_reference/README.md old mode 100644 new mode 100755 index 818cb2b..aa375a4 --- a/PHP/14_object_assigned_by_reference/README.md +++ b/PHP/14_object_assigned_by_reference/README.md @@ -1,77 +1,309 @@ -# Pattern: Object Assigned By Reference - -## Category - -Refrences - -## Definition - -## Instances - -### Instance 1 - -- CATEGORY: S0 -- FEATURE vs INTERNAL API: FEATURE -- INPUT SANITIZERS: NO -- SOURCES AND SINKS: NO -- NEGATIVE TEST CASES: NO -- CODE: - -```php -prop = &$x; - $x = $_GET["p1"]; - echo $obj->prop; -``` - -- MEASUREMENT: - -| Tool | RIPS | phpSAFE | WAP | Progpilot | Comm_1 | Comm_2 | Correct | -| ------------- | ---- | ------- | ---- | --------- | ------- | --------- | ------- | -| Vulnerability | NO | NO | NO | NO | YES | NO | YES | -Measurements Date: 8 June 2021 - -- OPCODE: - -```bash -$_main: ; (lines=16, args=0, vars=2, tmps=7) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/121_object_assigned_by_reference/121_object_assigned_by_reference.php:1-9 -L0 (3): NOP -L1 (5): EXT_STMT -L2 (5): ASSIGN CV0($x) string("safe") -L3 (6): EXT_STMT -L4 (6): ASSIGN CV1($obj) string("abc") -L5 (7): EXT_STMT -L6 (7): ASSIGN_OBJ_REF CV1($obj) string("prop") -L7 (7): OP_DATA CV0($x) -L8 (8): EXT_STMT -L9 (8): T5 = FETCH_R (global) string("_GET") -L10 (8): T6 = FETCH_DIM_R T5 string("p1") -L11 (8): ASSIGN CV0($x) T6 -L12 (9): EXT_STMT -L13 (9): T8 = FETCH_OBJ_R CV1($obj) string("prop") -L14 (9): ECHO T8 -L15 (9): RETURN int(1) -``` - -- DISCOVERY: - -```bash -cpg.call(".*ASSIGN_OBJ_REF.*").location.l -``` - -- PRECONDITIONS: - 1. - -- TRANSFORMATION: - -``` - -``` - +[//]: # (This file is automatically generated. If you wish to make any changes, please use the JSON files and regenerate this file using the tpframework.) + +# Object Assigned By Reference + +Tags: sast, php, php_v7.4.9 + +Version: v1.0 + +## Description + +Instance variables can be assigned by reference, every change to a variable referencing an instance variable of an object will be reflected in the instance variable and vice versa. + +## Overview + +| Instances | has discovery rule | discovery method | rule successfull | +|---------------------------|----------------------|--------------------|--------------------| +| [1 Instance](#1-instance) | yes | joern | yes | +| [2 Instance](#2-instance) | yes | joern | yes | +| [3 Instance](#3-instance) | yes | joern | yes | + +
+ + +## 1 Instance + + +The instance shows a variable assigned to an instance variable of an object by reference. The instance variable is first assigned to the other variable and than the other variable is set to a user controlled input. + +### Code + +```PHP +prop = &$x; // tarpit +$x = $_GET["p1"]; // source +echo $obj->prop; // sink +``` + +### Instance Properties + +| category | feature_vs_internal_api | input_sanitizer | negative_test_case | source_and_sink | +|------------|---------------------------|-------------------|----------------------|-------------------| +| S0 | FEATURE | no | no | no | + +
+ +More + +
+ + +### Compile + + +```bash +$_main: + ; (lines=12, args=0, vars=2, tmps=9) + ; (before optimizer) + ; /.../PHP/14_object_assigned_by_reference/1_instance_14_object_assigned_by_reference/1_instance_14_object_assigned_by_reference.php:1-9 + ; return [] RANGE[0..0] +0000 ASSIGN CV0($x) string("safe") +0001 V3 = NEW 0 string("myclass") +0002 DO_FCALL +0003 ASSIGN CV1($obj) V3 +0004 ASSIGN_OBJ_REF CV1($obj) string("prop") +0005 OP_DATA CV0($x) +0006 T7 = FETCH_R (global) string("_GET") +0007 T8 = FETCH_DIM_R T7 string("p1") +0008 ASSIGN CV0($x) T8 +0009 T10 = FETCH_OBJ_R CV1($obj) string("prop") +0010 ECHO T10 +0011 RETURN int(1) +LIVE RANGES: + 3: 0002 - 0003 (new) +``` + +
+ +
+ + +### Discovery + + +The rule searches for `ASSIGN_OBJ_REF` which is used to assign objects by reference. The rule is the same for all instances. + +```scala +val x14 = (name, "14_object_assigned_by_reference_iall", cpg.call(".*ASSIGN_OBJ_REF.*").location.toJson); +``` + +| discovery method | expected accuracy | +|--------------------|---------------------| +| joern | Perfect | + +
+ +
+ + +### Measurement + + +| Tool | Comm_1 | Comm_2 | Ground Truth | +|-------------|----------|----------|----------------| +| 17 May 2023 | yes | no | yes | + +
+ +
+ +
+ +
+ + +## 2 Instance + + +The instance shows a variable assigned to an instance variable of an object by reference. The other variable is first set to a user controlled input and than this variable is assigned to an instance variable of an object. + +### Code + +```PHP +prop = &$x; // tarpit +echo $obj->prop; // sink +``` + +### Instance Properties + +| category | feature_vs_internal_api | input_sanitizer | negative_test_case | source_and_sink | +|------------|---------------------------|-------------------|----------------------|-------------------| +| S0 | FEATURE | no | no | no | + +
+ +More + +
+ + +### Compile + + +```bash +$_main: + ; (lines=12, args=0, vars=2, tmps=9) + ; (before optimizer) + ; /.../PHP/14_object_assigned_by_reference/2_instance_14_object_assigned_by_reference/2_instance_14_object_assigned_by_reference.php:1-9 + ; return [] RANGE[0..0] +0000 ASSIGN CV0($x) string("safe") +0001 V3 = NEW 0 string("myclass") +0002 DO_FCALL +0003 ASSIGN CV1($obj) V3 +0004 T6 = FETCH_R (global) string("_GET") +0005 T7 = FETCH_DIM_R T6 string("p1") +0006 ASSIGN CV0($x) T7 +0007 ASSIGN_OBJ_REF CV1($obj) string("prop") +0008 OP_DATA CV0($x) +0009 T10 = FETCH_OBJ_R CV1($obj) string("prop") +0010 ECHO T10 +0011 RETURN int(1) +LIVE RANGES: + 3: 0002 - 0003 (new) +``` + +
+ +
+ + +### Discovery + + +The rule searches for `ASSIGN_OBJ_REF` which is used to assign objects by reference. The rule is the same for all instances. + +```scala +val x14 = (name, "14_object_assigned_by_reference_iall", cpg.call(".*ASSIGN_OBJ_REF.*").location.toJson); +``` + +| discovery method | expected accuracy | +|--------------------|---------------------| +| joern | Perfect | + +
+ +
+ + +### Measurement + + +| Tool | Comm_1 | Comm_2 | Ground Truth | +|-------------|----------|----------|----------------| +| 17 May 2023 | yes | yes | yes | + +
+ +
+ +
+ +
+ + +## 3 Instance + + +This instance provides another indirection through which the user input reaches the sink. In this instance, it is stored in an intermediate variable, which is assigned to another variable, which itself links to the instance variable, that is used for the output. + +### Code + +```PHP +prop = &$x; // tarpit +$x = $a; +echo $obj->prop; // sink +``` + +### Instance Properties + +| category | feature_vs_internal_api | input_sanitizer | negative_test_case | source_and_sink | +|------------|---------------------------|-------------------|----------------------|-------------------| +| S0 | FEATURE | no | no | no | + +
+ +More + +
+ + +### Compile + + +```bash +$_main: + ; (lines=13, args=0, vars=3, tmps=10) + ; (before optimizer) + ; /.../PHP/14_object_assigned_by_reference/3_instance_14_object_assigned_by_reference/3_instance_14_object_assigned_by_reference.php:1-10 + ; return [] RANGE[0..0] +0000 T3 = FETCH_R (global) string("_GET") +0001 T4 = FETCH_DIM_R T3 string("p1") +0002 ASSIGN CV0($a) T4 +0003 ASSIGN CV1($x) string("safe") +0004 V7 = NEW 0 string("myclass") +0005 DO_FCALL +0006 ASSIGN CV2($obj) V7 +0007 ASSIGN_OBJ_REF CV2($obj) string("prop") +0008 OP_DATA CV1($x) +0009 ASSIGN CV1($x) CV0($a) +0010 T12 = FETCH_OBJ_R CV2($obj) string("prop") +0011 ECHO T12 +0012 RETURN int(1) +LIVE RANGES: + 7: 0005 - 0006 (new) +``` + +
+ +
+ + +### Discovery + + +The rule searches for `ASSIGN_OBJ_REF` which is used to assign objects by reference. The rule is the same for all instances. + +```scala +val x14 = (name, "14_object_assigned_by_reference_iall", cpg.call(".*ASSIGN_OBJ_REF.*").location.toJson); +``` + +| discovery method | expected accuracy | +|--------------------|---------------------| +| joern | Perfect | + +
+ +
+ + +### Measurement + + +| Tool | Comm_1 | Comm_2 | Ground Truth | +|-------------|----------|----------|----------------| +| 17 May 2023 | yes | no | yes | + +
+ +
+ +
diff --git a/PHP/14_object_assigned_by_reference/docs/description.md b/PHP/14_object_assigned_by_reference/docs/description.md new file mode 100755 index 0000000..12e46a3 --- /dev/null +++ b/PHP/14_object_assigned_by_reference/docs/description.md @@ -0,0 +1 @@ +Instance variables can be assigned by reference, every change to a variable referencing an instance variable of an object will be reflected in the instance variable and vice versa. \ No newline at end of file diff --git a/PHP/14_object_assigned_by_reference/docs/discovery_notes.md b/PHP/14_object_assigned_by_reference/docs/discovery_notes.md new file mode 100755 index 0000000..2ce1236 --- /dev/null +++ b/PHP/14_object_assigned_by_reference/docs/discovery_notes.md @@ -0,0 +1 @@ +The rule searches for `ASSIGN_OBJ_REF` which is used to assign objects by reference. The rule is the same for all instances. \ No newline at end of file diff --git a/PHP/15_nested_function/15_nested_function.json b/PHP/15_nested_function/15_nested_function.json old mode 100644 new mode 100755 index 6ca130c..7f8f3b4 --- a/PHP/15_nested_function/15_nested_function.json +++ b/PHP/15_nested_function/15_nested_function.json @@ -1,15 +1,15 @@ -{ - "name": "Nested Function Declaration", - "description": "Declaring a function inside another function", - "family": "code_pattern_php", - "tags": [ - "sast", - "php", - "php_v7.4.9" - ], - "instances": [ - "./1_instance_15_nested_function/1_instance_15_nested_function.json", - "./2_instance_15_nested_function/2_instance_15_nested_function.json" - ], - "version": "v0.draft" +{ + "name": "Nested Function", + "description": "./docs/description.md", + "family": "code_pattern_php", + "tags": [ + "sast", + "php", + "php_v7.4.9" + ], + "instances": [ + "./1_instance_15_nested_function/1_instance_15_nested_function.json", + "./2_instance_15_nested_function/2_instance_15_nested_function.json" + ], + "version": "v1.0" } \ No newline at end of file diff --git a/PHP/15_nested_function/1_instance_15_nested_function/1_instance_15_nested_function.sc b/PHP/15_nested_function/15_nested_function.sc old mode 100644 new mode 100755 similarity index 97% rename from PHP/15_nested_function/1_instance_15_nested_function/1_instance_15_nested_function.sc rename to PHP/15_nested_function/15_nested_function.sc index 190f1a7..3f3ef16 --- a/PHP/15_nested_function/1_instance_15_nested_function/1_instance_15_nested_function.sc +++ b/PHP/15_nested_function/15_nested_function.sc @@ -1,6 +1,6 @@ -@main def main(name : String): Unit = { - importCpg(name) - val x15 = (name, "15_nested_function_iall", cpg.call(".*DECLARE_FUNCTION.*").location.toJson); - println(x15) - delete; +@main def main(name : String): Unit = { + importCpg(name) + val x15 = (name, "15_nested_function_iall", cpg.call(".*DECLARE_FUNCTION.*").location.toJson); + println(x15) + delete; } \ No newline at end of file diff --git a/PHP/15_nested_function/1_instance_15_nested_function/1_instance_15_nested_function.bash b/PHP/15_nested_function/1_instance_15_nested_function/1_instance_15_nested_function.bash old mode 100644 new mode 100755 index 733559e..9a5cc1c --- a/PHP/15_nested_function/1_instance_15_nested_function/1_instance_15_nested_function.bash +++ b/PHP/15_nested_function/1_instance_15_nested_function/1_instance_15_nested_function.bash @@ -1,35 +1,32 @@ - -$_main: ; (lines=12, args=0, vars=1, tmps=5) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/9_nested_function/9_nested_function.php:1-10 -L0 (8): EXT_STMT -L1 (8): T1 = FETCH_R (global) string("_GET") -L2 (8): T2 = FETCH_DIM_R T1 string("p1") -L3 (8): ASSIGN CV0($b) T2 -L4 (9): EXT_STMT -L5 (9): INIT_FCALL 0 80 string("f") -L6 (9): DO_FCALL -L7 (10): EXT_STMT -L8 (10): INIT_FCALL_BY_NAME 1 string("D") -L9 (10): SEND_VAR_EX CV0($b) 1 -L10 (10): DO_FCALL -L11 (10): RETURN int(1) - -F: ; (lines=5, args=0, vars=0, tmps=0) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/9_nested_function/9_nested_function.php:3-7 -L0 (3): EXT_NOP -L1 (4): EXT_STMT -L2 (4): DECLARE_FUNCTION string("d") -L3 (7): EXT_STMT -L4 (7): RETURN null - -D: ; (lines=6, args=1, vars=1, tmps=0) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/9_nested_function/9_nested_function.php:4-6 -L0 (4): EXT_NOP -L1 (4): CV0($b) = RECV 1 -L2 (5): EXT_STMT -L3 (5): ECHO CV0($b) -L4 (6): EXT_STMT -L5 (6): RETURN null + +$_main: + ; (lines=9, args=0, vars=1, tmps=5) + ; (before optimizer) + ; /.../PHP/15_nested_function/1_instance_15_nested_function/1_instance_15_nested_function.php:1-9 + ; return [] RANGE[0..0] +0000 T1 = FETCH_R (global) string("_GET") +0001 T2 = FETCH_DIM_R T1 string("p1") +0002 ASSIGN CV0($b) T2 +0003 INIT_FCALL 0 80 string("f") +0004 DO_UCALL +0005 INIT_FCALL_BY_NAME 1 string("D") +0006 SEND_VAR_EX CV0($b) 1 +0007 DO_FCALL_BY_NAME +0008 RETURN int(1) + +F: + ; (lines=2, args=0, vars=0, tmps=0) + ; (before optimizer) + ; /.../PHP/15_nested_function/1_instance_15_nested_function/1_instance_15_nested_function.php:2-6 + ; return [] RANGE[0..0] +0000 DECLARE_FUNCTION string("d") 0 +0001 RETURN null + +D: + ; (lines=3, args=1, vars=1, tmps=0) + ; (before optimizer) + ; /.../PHP/15_nested_function/1_instance_15_nested_function/1_instance_15_nested_function.php:3-5 + ; return [] RANGE[0..0] +0000 CV0($b) = RECV 1 +0001 ECHO CV0($b) +0002 RETURN null diff --git a/PHP/15_nested_function/1_instance_15_nested_function/1_instance_15_nested_function.json b/PHP/15_nested_function/1_instance_15_nested_function/1_instance_15_nested_function.json old mode 100644 new mode 100755 index af8f3dd..d2c0def --- a/PHP/15_nested_function/1_instance_15_nested_function/1_instance_15_nested_function.json +++ b/PHP/15_nested_function/1_instance_15_nested_function/1_instance_15_nested_function.json @@ -1,37 +1,38 @@ -{ - "code": { - "path": "./1_instance_15_nested_function.php", - "injection_skeleton_broken": true - }, - "discovery": { - "rule": "./1_instance_15_nested_function.sc", - "method": "joern", - "rule_accuracy": "Perfect", - "notes": null - }, - "compile": { - "binary": "./1_instance_15_nested_function.bash", - "instruction": null, - "dependencies": null - }, - "expectation": { - "type": "xss", - "sink_file": "./1_instance_15_nested_function.php", - "sink_line": 5, - "source_file": "./1_instance_15_nested_function.php", - "source_line": 8, - "expectation": true - }, - "properties": { - "category": "S0", - "feature_vs_internal_api": "FEATURE", - "input_sanitizer": false, - "source_and_sink": false, - "negative_test_case": false - }, - "remediation": { - "notes": "", - "transformation": null, - "modeling_rule": null - } +{ + "description": "This instance defines a function within another function. The sink can be found within the inner function.", + "code": { + "path": "./1_instance_15_nested_function.php", + "injection_skeleton_broken": true + }, + "discovery": { + "rule": "../15_nested_function.sc", + "method": "joern", + "rule_accuracy": "Perfect", + "notes": "The rule searches for `DECLARE_FUNCTION`." + }, + "compile": { + "binary": "./1_instance_15_nested_function.bash", + "instruction": null, + "dependencies": null + }, + "expectation": { + "type": "xss", + "sink_file": "./1_instance_15_nested_function.php", + "sink_line": 4, + "source_file": "./1_instance_15_nested_function.php", + "source_line": 7, + "expectation": true + }, + "properties": { + "category": "S0", + "feature_vs_internal_api": "FEATURE", + "input_sanitizer": false, + "source_and_sink": false, + "negative_test_case": false + }, + "remediation": { + "notes": "If possible, the inner function should be declared outside of the scope of the outer function. Are there any performance related or other issues with that? If not, this could be automated.", + "transformation": null, + "modeling_rule": null + } } \ No newline at end of file diff --git a/PHP/15_nested_function/1_instance_15_nested_function/1_instance_15_nested_function.php b/PHP/15_nested_function/1_instance_15_nested_function/1_instance_15_nested_function.php old mode 100644 new mode 100755 index 65be58c..f32af9b --- a/PHP/15_nested_function/1_instance_15_nested_function/1_instance_15_nested_function.php +++ b/PHP/15_nested_function/1_instance_15_nested_function/1_instance_15_nested_function.php @@ -1,10 +1,9 @@ - + + +## 1 Instance + + +This instance defines a function within another function. The sink can be found within the inner function. + +### Code + +```PHP + + +More + +
+ + +### Compile + + +```bash +$_main: + ; (lines=9, args=0, vars=1, tmps=5) + ; (before optimizer) + ; /.../PHP/15_nested_function/1_instance_15_nested_function/1_instance_15_nested_function.php:1-9 + ; return [] RANGE[0..0] +0000 T1 = FETCH_R (global) string("_GET") +0001 T2 = FETCH_DIM_R T1 string("p1") +0002 ASSIGN CV0($b) T2 +0003 INIT_FCALL 0 80 string("f") +0004 DO_UCALL +0005 INIT_FCALL_BY_NAME 1 string("D") +0006 SEND_VAR_EX CV0($b) 1 +0007 DO_FCALL_BY_NAME +0008 RETURN int(1) + +F: + ; (lines=2, args=0, vars=0, tmps=0) + ; (before optimizer) + ; /.../PHP/15_nested_function/1_instance_15_nested_function/1_instance_15_nested_function.php:2-6 + ; return [] RANGE[0..0] +0000 DECLARE_FUNCTION string("d") 0 +0001 RETURN null + +D: + ; (lines=3, args=1, vars=1, tmps=0) + ; (before optimizer) + ; /.../PHP/15_nested_function/1_instance_15_nested_function/1_instance_15_nested_function.php:3-5 + ; return [] RANGE[0..0] +0000 CV0($b) = RECV 1 +0001 ECHO CV0($b) +0002 RETURN null +``` + +
+ +
+ + +### Discovery + + +The rule searches for `DECLARE_FUNCTION`. + +```scala +val x15 = (name, "15_nested_function_iall", cpg.call(".*DECLARE_FUNCTION.*").location.toJson); +``` + +| discovery method | expected accuracy | +|--------------------|---------------------| +| joern | Perfect | + +
+ +
+ + +### Measurement + + +| Tool | Comm_1 | Comm_2 | phpSAFE | Progpilot | RIPS | WAP | Ground Truth | +|-------------|----------|----------|-----------|-------------|--------|-------|----------------| +| 08 Jun 2021 | yes | no | yes | yes | no | yes | yes | +| 17 May 2023 | yes | no | | | | | yes | + +
+ +
+ + +### Remediation + + +If possible, the inner function should be declared outside of the scope of the outer function. Are there any performance related or other issues with that? If not, this could be automated. + +
+ + + + + +
+ + +## 2 Instance + + +This instance defines a function within another function. The sink is moved out of the inner function to provide the standard skeleton. Why does this make a difference for some SAST tools? + +### Code + +```PHP + + +More + +
+ + +### Compile + + +```bash +$_main: + ; (lines=11, args=0, vars=2, tmps=6) + ; (before optimizer) + ; /.../PHP/15_nested_function/2_instance_15_nested_function/2_instance_15_nested_function.php:1-10 + ; return [] RANGE[0..0] +0000 T2 = FETCH_R (global) string("_GET") +0001 T3 = FETCH_DIM_R T2 string("p1") +0002 ASSIGN CV0($a) T3 +0003 INIT_FCALL 0 80 string("f") +0004 DO_UCALL +0005 INIT_FCALL_BY_NAME 1 string("D") +0006 SEND_VAR_EX CV0($a) 1 +0007 V6 = DO_FCALL_BY_NAME +0008 ASSIGN CV1($b) V6 +0009 ECHO CV1($b) +0010 RETURN int(1) + +F: + ; (lines=2, args=0, vars=0, tmps=0) + ; (before optimizer) + ; /.../PHP/15_nested_function/2_instance_15_nested_function/2_instance_15_nested_function.php:2-6 + ; return [] RANGE[0..0] +0000 DECLARE_FUNCTION string("d") 0 +0001 RETURN null + +D: + ; (lines=3, args=1, vars=1, tmps=0) + ; (before optimizer) + ; /.../PHP/15_nested_function/2_instance_15_nested_function/2_instance_15_nested_function.php:3-5 + ; return [] RANGE[0..0] +0000 CV0($c) = RECV 1 +0001 RETURN CV0($c) +0002 RETURN null +``` + +
+ +
+ + +### Discovery + + +The rule searches for `DECLARE_FUNCTION`. + +```scala +val x15 = (name, "15_nested_function_iall", cpg.call(".*DECLARE_FUNCTION.*").location.toJson); +``` + +| discovery method | expected accuracy | +|--------------------|---------------------| +| joern | Perfect | + +
+ +
+ + +### Measurement + + +| Tool | Comm_1 | Comm_2 | Ground Truth | +|-------------|----------|----------|----------------| +| 17 May 2023 | yes | yes | yes | + +
+ +
+ + +### Remediation + + +If possible, the inner function should be declared outside of the scope of the outer function. Are there any performance related or other issues with that? If not, this could be automated. + +
+ +
+ + diff --git a/PHP/15_nested_function/docs/description.md b/PHP/15_nested_function/docs/description.md new file mode 100755 index 0000000..017e699 --- /dev/null +++ b/PHP/15_nested_function/docs/description.md @@ -0,0 +1 @@ +PHP allows declaring a function inside another function. \ No newline at end of file diff --git a/PHP/16_variadic_functions/16_variadic_functions.json b/PHP/16_variadic_functions/16_variadic_functions.json old mode 100644 new mode 100755 index 4f6d9be..758f8fd --- a/PHP/16_variadic_functions/16_variadic_functions.json +++ b/PHP/16_variadic_functions/16_variadic_functions.json @@ -1,16 +1,16 @@ -{ - "name": "Variadic Functions", - "description": "", - "family": "code_pattern_php", - "tags": [ - "sast", - "php", - "php_v7.4.9" - ], - "instances": [ - "./1_instance_16_variadic_functions/1_instance_16_variadic_functions.json", - "./2_instance_16_variadic_functions/2_instance_16_variadic_functions.json", - "./3_instance_16_variadic_functions/3_instance_16_variadic_functions.json" - ], - "version": "v0.draft" +{ + "name": "Variadic Functions", + "description": "./docs/description.md", + "family": "code_pattern_php", + "tags": [ + "sast", + "php", + "php_v7.4.9" + ], + "instances": [ + "./1_instance_16_variadic_functions/1_instance_16_variadic_functions.json", + "./2_instance_16_variadic_functions/2_instance_16_variadic_functions.json", + "./3_instance_16_variadic_functions/3_instance_16_variadic_functions.json" + ], + "version": "v1.0" } \ No newline at end of file diff --git a/PHP/16_variadic_functions/1_instance_16_variadic_functions/1_instance_16_variadic_functions.sc b/PHP/16_variadic_functions/16_variadic_functions.sc old mode 100644 new mode 100755 similarity index 97% rename from PHP/16_variadic_functions/1_instance_16_variadic_functions/1_instance_16_variadic_functions.sc rename to PHP/16_variadic_functions/16_variadic_functions.sc index d79b30a..9191684 --- a/PHP/16_variadic_functions/1_instance_16_variadic_functions/1_instance_16_variadic_functions.sc +++ b/PHP/16_variadic_functions/16_variadic_functions.sc @@ -1,6 +1,6 @@ -@main def main(name : String): Unit = { - importCpg(name) - val x16 = (name, "16_variadic_functions_iall", cpg.call(".*RECV_VARIADIC.*").location.toJson); - println(x16) - delete; +@main def main(name : String): Unit = { + importCpg(name) + val x16 = (name, "16_variadic_functions_iall", cpg.call(".*RECV_VARIADIC.*").location.toJson); + println(x16) + delete; } \ No newline at end of file diff --git a/PHP/16_variadic_functions/1_instance_16_variadic_functions/1_instance_16_variadic_functions.bash b/PHP/16_variadic_functions/1_instance_16_variadic_functions/1_instance_16_variadic_functions.bash old mode 100644 new mode 100755 index 62cea8c..ddb3727 --- a/PHP/16_variadic_functions/1_instance_16_variadic_functions/1_instance_16_variadic_functions.bash +++ b/PHP/16_variadic_functions/1_instance_16_variadic_functions/1_instance_16_variadic_functions.bash @@ -1,33 +1,31 @@ - -$_main: ; (lines=12, args=0, vars=1, tmps=4) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/10_variadic_functions/10_variadic_functions.php:1-12 -L0 (11): EXT_STMT -L1 (11): T1 = FETCH_R (global) string("_GET") -L2 (11): T2 = FETCH_DIM_R T1 string("p1") -L3 (11): ASSIGN CV0($b) T2 -L4 (12): EXT_STMT -L5 (12): INIT_FCALL 4 192 string("sum") -L6 (12): SEND_VAL int(1) 1 -L7 (12): SEND_VAL int(2) 2 -L8 (12): SEND_VAL int(3) 3 -L9 (12): SEND_VAR CV0($b) 4 -L10 (12): DO_FCALL -L11 (12): RETURN int(1) - -sum: ; (lines=11, args=0, vars=2, tmps=1) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/10_variadic_functions/10_variadic_functions.php:4-9 -L0 (4): EXT_NOP -L1 (4): CV0($numbers) = RECV_VARIADIC 1 -L2 (5): EXT_STMT -L3 (5): V2 = FE_RESET_R CV0($numbers) L8 -L4 (5): FE_FETCH_R V2 CV1($n) L8 -L5 (7): EXT_STMT -L6 (7): ECHO CV1($n) -L7 (5): JMP L4 -L8 (5): FE_FREE V2 -L9 (9): EXT_STMT -L10 (9): RETURN null -LIVE RANGES: - 2: L4 - L8 (loop) \ No newline at end of file + +$_main: + ; (lines=10, args=0, vars=1, tmps=4) + ; (before optimizer) + ; /.../PHP/16_variadic_functions/1_instance_16_variadic_functions/1_instance_16_variadic_functions.php:1-11 + ; return [] RANGE[0..0] +0000 T1 = FETCH_R (global) string("_GET") +0001 T2 = FETCH_DIM_R T1 string("p1") +0002 ASSIGN CV0($a) T2 +0003 INIT_FCALL 4 192 string("sum") +0004 SEND_VAL int(1) 1 +0005 SEND_VAL int(2) 2 +0006 SEND_VAL int(3) 3 +0007 SEND_VAR CV0($a) 4 +0008 DO_UCALL +0009 RETURN int(1) + +sum: + ; (lines=7, args=0, vars=2, tmps=1) + ; (before optimizer) + ; /.../PHP/16_variadic_functions/1_instance_16_variadic_functions/1_instance_16_variadic_functions.php:3-8 + ; return [] RANGE[0..0] +0000 CV0($numbers) = RECV_VARIADIC 1 +0001 V2 = FE_RESET_R CV0($numbers) 0005 +0002 FE_FETCH_R V2 CV1($n) 0005 +0003 ECHO CV1($n) +0004 JMP 0002 +0005 FE_FREE V2 +0006 RETURN null +LIVE RANGES: + 2: 0002 - 0005 (loop) diff --git a/PHP/16_variadic_functions/1_instance_16_variadic_functions/1_instance_16_variadic_functions.json b/PHP/16_variadic_functions/1_instance_16_variadic_functions/1_instance_16_variadic_functions.json old mode 100644 new mode 100755 index 4c66fba..e2f838c --- a/PHP/16_variadic_functions/1_instance_16_variadic_functions/1_instance_16_variadic_functions.json +++ b/PHP/16_variadic_functions/1_instance_16_variadic_functions/1_instance_16_variadic_functions.json @@ -1,37 +1,38 @@ -{ - "code": { - "path": "./1_instance_16_variadic_functions.php", - "injection_skeleton_broken": true - }, - "discovery": { - "rule": "./1_instance_16_variadic_functions.sc", - "method": "joern", - "rule_accuracy": null, - "notes": null - }, - "compile": { - "binary": "./1_instance_16_variadic_functions.bash", - "instruction": null, - "dependencies": null - }, - "expectation": { - "type": "xss", - "sink_file": "./1_instance_16_variadic_functions.php", - "sink_line": 7, - "source_file": "./1_instance_16_variadic_functions.php", - "source_line": 11, - "expectation": true - }, - "properties": { - "category": "S0", - "feature_vs_internal_api": "FEATURE", - "input_sanitizer": false, - "source_and_sink": false, - "negative_test_case": false - }, - "remediation": { - "notes": "", - "transformation": null, - "modeling_rule": null - } +{ + "description": "This instance shows a function, that takes an arbitrary number of variables and outputs each of them. The sink is within the function.", + "code": { + "path": "./1_instance_16_variadic_functions.php", + "injection_skeleton_broken": true + }, + "discovery": { + "rule": "../16_variadic_functions.sc", + "method": "joern", + "rule_accuracy": "Perfect", + "notes": "The rule searches for `RECV_VARIADIC`." + }, + "compile": { + "binary": "./1_instance_16_variadic_functions.bash", + "instruction": null, + "dependencies": null + }, + "expectation": { + "type": "xss", + "sink_file": "./1_instance_16_variadic_functions.php", + "sink_line": 6, + "source_file": "./1_instance_16_variadic_functions.php", + "source_line": 10, + "expectation": true + }, + "properties": { + "category": "S0", + "feature_vs_internal_api": "FEATURE", + "input_sanitizer": false, + "source_and_sink": false, + "negative_test_case": false + }, + "remediation": { + "notes": "", + "transformation": null, + "modeling_rule": null + } } \ No newline at end of file diff --git a/PHP/16_variadic_functions/1_instance_16_variadic_functions/1_instance_16_variadic_functions.php b/PHP/16_variadic_functions/1_instance_16_variadic_functions/1_instance_16_variadic_functions.php old mode 100644 new mode 100755 index 5460d67..e5b75f3 --- a/PHP/16_variadic_functions/1_instance_16_variadic_functions/1_instance_16_variadic_functions.php +++ b/PHP/16_variadic_functions/1_instance_16_variadic_functions/1_instance_16_variadic_functions.php @@ -1,12 +1,11 @@ - + + +## 1 Instance + + +This instance shows a function, that takes an arbitrary number of variables and outputs each of them. The sink is within the function. + +### Code + +```PHP + + +More + +
+ + +### Compile + + +```bash +$_main: + ; (lines=10, args=0, vars=1, tmps=4) + ; (before optimizer) + ; /.../PHP/16_variadic_functions/1_instance_16_variadic_functions/1_instance_16_variadic_functions.php:1-11 + ; return [] RANGE[0..0] +0000 T1 = FETCH_R (global) string("_GET") +0001 T2 = FETCH_DIM_R T1 string("p1") +0002 ASSIGN CV0($a) T2 +0003 INIT_FCALL 4 192 string("sum") +0004 SEND_VAL int(1) 1 +0005 SEND_VAL int(2) 2 +0006 SEND_VAL int(3) 3 +0007 SEND_VAR CV0($a) 4 +0008 DO_UCALL +0009 RETURN int(1) + +sum: + ; (lines=7, args=0, vars=2, tmps=1) + ; (before optimizer) + ; /.../PHP/16_variadic_functions/1_instance_16_variadic_functions/1_instance_16_variadic_functions.php:3-8 + ; return [] RANGE[0..0] +0000 CV0($numbers) = RECV_VARIADIC 1 +0001 V2 = FE_RESET_R CV0($numbers) 0005 +0002 FE_FETCH_R V2 CV1($n) 0005 +0003 ECHO CV1($n) +0004 JMP 0002 +0005 FE_FREE V2 +0006 RETURN null +LIVE RANGES: + 2: 0002 - 0005 (loop) +``` + +
+ +
+ + +### Discovery + + +The rule searches for `RECV_VARIADIC`. + +```scala +val x16 = (name, "16_variadic_functions_iall", cpg.call(".*RECV_VARIADIC.*").location.toJson); +``` + +| discovery method | expected accuracy | +|--------------------|---------------------| +| joern | Perfect | + +
+ +
+ + +### Measurement + + +| Tool | Comm_1 | Comm_2 | phpSAFE | Progpilot | RIPS | WAP | Ground Truth | +|-------------|----------|----------|-----------|-------------|--------|-------|----------------| +| 08 Jun 2021 | yes | yes | no | no | no | no | yes | +| 17 May 2023 | yes | yes | | | | | yes | + +
+ + + + + +
+ + +## 2 Instance + + +The instance shows a function, that takes an arbitrary number of arguments and returns the fourth, which contains a user controlled value. + +### Code + +```PHP + + +More + +
+ + +### Compile + + +```bash +$_main: + ; (lines=12, args=0, vars=2, tmps=5) + ; (before optimizer) + ; /.../PHP/16_variadic_functions/2_instance_16_variadic_functions/2_instance_16_variadic_functions.php:1-8 + ; return [] RANGE[0..0] +0000 T2 = FETCH_R (global) string("_GET") +0001 T3 = FETCH_DIM_R T2 string("p1") +0002 ASSIGN CV0($a) T3 +0003 INIT_FCALL 4 176 string("sum") +0004 SEND_VAL int(1) 1 +0005 SEND_VAL int(2) 2 +0006 SEND_VAL int(3) 3 +0007 SEND_VAR CV0($a) 4 +0008 V5 = DO_UCALL +0009 ASSIGN CV1($b) V5 +0010 ECHO CV1($b) +0011 RETURN int(1) + +sum: + ; (lines=4, args=0, vars=1, tmps=1) + ; (before optimizer) + ; /.../PHP/16_variadic_functions/2_instance_16_variadic_functions/2_instance_16_variadic_functions.php:3-5 + ; return [] RANGE[0..0] +0000 CV0($numbers) = RECV_VARIADIC 1 +0001 T1 = FETCH_DIM_R CV0($numbers) int(3) +0002 RETURN T1 +0003 RETURN null +``` + +
+ +
+ + +### Discovery + + +The rule searches for `RECV_VARIADIC`. + +```scala +val x16 = (name, "16_variadic_functions_iall", cpg.call(".*RECV_VARIADIC.*").location.toJson); +``` + +| discovery method | expected accuracy | +|--------------------|---------------------| +| joern | Perfect | + +
+ +
+ + +### Measurement + + +| Tool | Comm_1 | Comm_2 | Ground Truth | +|-------------|----------|----------|----------------| +| 17 May 2023 | yes | yes | yes | + +
+ +
+ + + +
+ + +## 3 Instance + + +The instance shows a function, that takes an arbitrary number of arguments and returns the first argument, that is not 1, so if the user controlled value is something different than 1, it ends up in the sink. + +### Code + +```PHP + + +More + +
+ + +### Compile + + +```bash +$_main: + ; (lines=12, args=0, vars=2, tmps=5) + ; (before optimizer) + ; /.../PHP/16_variadic_functions/3_instance_16_variadic_functions/3_instance_16_variadic_functions.php:1-14 + ; return [] RANGE[0..0] +0000 T2 = FETCH_R (global) string("_GET") +0001 T3 = FETCH_DIM_R T2 string("p1") +0002 ASSIGN CV0($a) T3 +0003 INIT_FCALL 4 208 string("sum") +0004 SEND_VAL int(1) 1 +0005 SEND_VAL int(1) 2 +0006 SEND_VAL int(1) 3 +0007 SEND_VAR CV0($a) 4 +0008 V5 = DO_UCALL +0009 ASSIGN CV1($b) V5 +0010 ECHO CV1($b) +0011 RETURN int(1) + +sum: + ; (lines=11, args=0, vars=2, tmps=2) + ; (before optimizer) + ; /.../PHP/16_variadic_functions/3_instance_16_variadic_functions/3_instance_16_variadic_functions.php:3-10 + ; return [] RANGE[0..0] +0000 CV0($numbers) = RECV_VARIADIC 1 +0001 V2 = FE_RESET_R CV0($numbers) 0008 +0002 FE_FETCH_R V2 CV1($n) 0008 +0003 T3 = IS_NOT_EQUAL CV1($n) int(1) +0004 JMPZ T3 0007 +0005 FE_FREE V2 +0006 RETURN CV1($n) +0007 JMP 0002 +0008 FE_FREE V2 +0009 RETURN string("safe") +0010 RETURN null +LIVE RANGES: + 2: 0002 - 0008 (loop) +``` + +
+ +
+ + +### Discovery + + +The rule searches for `RECV_VARIADIC`. + +```scala +val x16 = (name, "16_variadic_functions_iall", cpg.call(".*RECV_VARIADIC.*").location.toJson); +``` + +| discovery method | expected accuracy | +|--------------------|---------------------| +| joern | Perfect | + +
+ +
+ + +### Measurement + + +| Tool | Comm_1 | Comm_2 | Ground Truth | +|-------------|----------|----------|----------------| +| 17 May 2023 | yes | yes | yes | + +
+ +
+ + diff --git a/PHP/16_variadic_functions/docs/description.md b/PHP/16_variadic_functions/docs/description.md new file mode 100755 index 0000000..b89e757 --- /dev/null +++ b/PHP/16_variadic_functions/docs/description.md @@ -0,0 +1 @@ +Variadic functions in PHP allow a function to accept an arbitrary number of arguments, which are automatically collected into an array, using the ... (ellipsis) operator. \ No newline at end of file diff --git a/PHP/17_get_arguments/17_get_arguments.json b/PHP/17_get_arguments/17_get_arguments.json old mode 100644 new mode 100755 index 9042832..5b23749 --- a/PHP/17_get_arguments/17_get_arguments.json +++ b/PHP/17_get_arguments/17_get_arguments.json @@ -1,15 +1,15 @@ -{ - "name": "Get Arguments", - "description": "In PHP it is not possible to define more than one function with the same name, even if the number of arguments are different between the two functions. \n```php\n + + +## 1 Instance + + +This instance uses `func_get_args` and iterates over all the arguments passed to the function and outputs those. The sink is within this for loop. + +### Code + +```PHP + + +More + +
+ + +### Compile + + +```bash +$_main: + ; (lines=10, args=0, vars=1, tmps=4) + ; (before optimizer) + ; /.../PHP/17_get_arguments/1_instance_17_get_arguments/1_instance_17_get_arguments.php:1-11 + ; return [] RANGE[0..0] +0000 T1 = FETCH_R (global) string("_GET") +0001 T2 = FETCH_DIM_R T1 string("p1") +0002 ASSIGN CV0($b) T2 +0003 INIT_FCALL 4 192 string("sum") +0004 SEND_VAL int(1) 1 +0005 SEND_VAL int(2) 2 +0006 SEND_VAL int(3) 3 +0007 SEND_VAR CV0($b) 4 +0008 DO_UCALL +0009 RETURN int(1) + +sum: + ; (lines=7, args=0, vars=1, tmps=2) + ; (before optimizer) + ; /.../PHP/17_get_arguments/1_instance_17_get_arguments/1_instance_17_get_arguments.php:2-8 + ; return [] RANGE[0..0] +0000 T1 = FUNC_GET_ARGS +0001 V2 = FE_RESET_R T1 0005 +0002 FE_FETCH_R V2 CV0($n) 0005 +0003 ECHO CV0($n) +0004 JMP 0002 +0005 FE_FREE V2 +0006 RETURN null +LIVE RANGES: + 2: 0002 - 0005 (loop) +``` + +
+ +
+ + +### Discovery + + +In opcode there is a special opcode for this function `FUNC_GET_ARGS`. + +```scala +val x17 = (name, "17_get_arguments_iall", cpg.call(".*FUNC_GET_ARGS.*").location.toJson); +``` + +| discovery method | expected accuracy | +|--------------------|---------------------| +| joern | Perfect | + +
+ +
+ + +### Measurement + + +| Tool | Comm_1 | Comm_2 | phpSAFE | Progpilot | RIPS | WAP | Ground Truth | +|-------------|----------|----------|-----------|-------------|--------|-------|----------------| +| 08 Jun 2021 | no | yes | no | no | yes | no | yes | +| 17 May 2023 | no | yes | | | | | yes | + +
+ + + + + +
+ + +## 2 Instance + + +This instance uses `func_get_args` to output the fourth element, which contains user controlled input. + +### Code + +```PHP + + +More + +
+ + +### Compile + + +```bash +$_main: + ; (lines=12, args=0, vars=2, tmps=5) + ; (before optimizer) + ; /.../PHP/17_get_arguments/2_instance_17_get_arguments/2_instance_17_get_arguments.php:1-7 + ; return [] RANGE[0..0] +0000 T2 = FETCH_R (global) string("_GET") +0001 T3 = FETCH_DIM_R T2 string("p1") +0002 ASSIGN CV0($a) T3 +0003 INIT_FCALL 4 176 string("fourth") +0004 SEND_VAL int(1) 1 +0005 SEND_VAL int(2) 2 +0006 SEND_VAL int(3) 3 +0007 SEND_VAR CV0($a) 4 +0008 V5 = DO_UCALL +0009 ASSIGN CV1($b) V5 +0010 ECHO CV1($b) +0011 RETURN int(1) + +fourth: + ; (lines=4, args=0, vars=0, tmps=2) + ; (before optimizer) + ; /.../PHP/17_get_arguments/2_instance_17_get_arguments/2_instance_17_get_arguments.php:2-4 + ; return [] RANGE[0..0] +0000 T0 = FUNC_GET_ARGS +0001 T1 = FETCH_DIM_R T0 int(3) +0002 RETURN T1 +0003 RETURN null +``` + +
+ +
+ + +### Discovery + + +In opcode there is a special opcode for this function `FUNC_GET_ARGS`. + +```scala +val x17 = (name, "17_get_arguments_iall", cpg.call(".*FUNC_GET_ARGS.*").location.toJson); +``` + +| discovery method | expected accuracy | +|--------------------|---------------------| +| joern | Perfect | + +
+ +
+ + +### Measurement + + +| Tool | Comm_1 | Comm_2 | Ground Truth | +|-------------|----------|----------|----------------| +| 17 May 2023 | no | yes | yes | + +
+ +
+ + diff --git a/PHP/17_get_arguments/docs/description.md b/PHP/17_get_arguments/docs/description.md new file mode 100755 index 0000000..bfa58d2 --- /dev/null +++ b/PHP/17_get_arguments/docs/description.md @@ -0,0 +1,30 @@ +In PHP it is not possible to define more than one function with the same name, even if the number of arguments are different between the two functions. + +```php + + + +## 1 Instance + + +The instance uses the unpack operator for calling the `add` function. The sink is within this function. + +### Code + +```PHP + + +More + +
+ + +### Compile + + +```bash +$_main: + ; (lines=11, args=0, vars=2, tmps=6) + ; (before optimizer) + ; /.../PHP/18_send_unpack/1_instance_18_send_unpack/1_instance_18_send_unpack.php:1-11 + ; return [] RANGE[0..0] +0000 T2 = FETCH_R (global) string("_GET") +0001 T3 = FETCH_DIM_R T2 string("p1") +0002 ASSIGN CV0($b) T3 +0003 T5 = INIT_ARRAY 2 (packed) int(1) NEXT +0004 T5 = ADD_ARRAY_ELEMENT CV0($b) NEXT +0005 ASSIGN CV1($a) T5 +0006 INIT_FCALL 0 112 string("add") +0007 SEND_UNPACK CV1($a) +0008 CHECK_UNDEF_ARGS +0009 DO_UCALL +0010 RETURN int(1) +LIVE RANGES: + 5: 0004 - 0005 (tmp/var) + +add: + ; (lines=5, args=2, vars=2, tmps=0) + ; (before optimizer) + ; /.../PHP/18_send_unpack/1_instance_18_send_unpack/1_instance_18_send_unpack.php:3-7 + ; return [] RANGE[0..0] +0000 CV0($a) = RECV 1 +0001 CV1($b) = RECV 2 +0002 ECHO CV0($a) +0003 ECHO CV1($b) +0004 RETURN null +``` + +
+ +
+ + +### Discovery + + +The unpack operator has an equivalent opcode instruction `SEND_UNPACK`. This rule searches for that. + +```scala +val x18 = (name, "18_send_unpack_iall", cpg.call(".*SEND_UNPACK.*").location.toJson); +``` + +| discovery method | expected accuracy | +|--------------------|---------------------| +| joern | Perfect | + +
+ +
+ + +### Measurement + + +| Tool | Comm_1 | Comm_2 | phpSAFE | Progpilot | RIPS | WAP | Ground Truth | +|-------------|----------|----------|-----------|-------------|--------|-------|----------------| +| 08 Jun 2021 | yes | no | yes | no | yes | no | yes | +| 17 May 2023 | yes | yes | | | | | yes | + +
+ + + + + +
+ + +## 2 Instance + + +The instance uses the unpack operator to call the `add` function, but this time, one + +### Code + +```PHP + + +More + +
+ + +### Compile + + +```bash +$_main: + ; (lines=13, args=0, vars=3, tmps=7) + ; (before optimizer) + ; /.../PHP/18_send_unpack/2_instance_18_send_unpack/2_instance_18_send_unpack.php:1-10 + ; return [] RANGE[0..0] +0000 T3 = FETCH_R (global) string("_GET") +0001 T4 = FETCH_DIM_R T3 string("p1") +0002 ASSIGN CV0($b) T4 +0003 T6 = INIT_ARRAY 2 (packed) int(1) NEXT +0004 T6 = ADD_ARRAY_ELEMENT CV0($b) NEXT +0005 ASSIGN CV1($a) T6 +0006 INIT_FCALL 0 112 string("add") +0007 SEND_UNPACK CV1($a) +0008 CHECK_UNDEF_ARGS +0009 V8 = DO_UCALL +0010 ASSIGN CV2($c) V8 +0011 ECHO CV2($c) +0012 RETURN int(1) +LIVE RANGES: + 6: 0004 - 0005 (tmp/var) + +add: + ; (lines=4, args=2, vars=2, tmps=0) + ; (before optimizer) + ; /.../PHP/18_send_unpack/2_instance_18_send_unpack/2_instance_18_send_unpack.php:3-5 + ; return [] RANGE[0..0] +0000 CV0($a) = RECV 1 +0001 CV1($b) = RECV 2 +0002 RETURN CV1($b) +0003 RETURN null +``` + +
+ +
+ + +### Discovery + + +The unpack operator has an equivalent opcode instruction `SEND_UNPACK`. This rule searches for that. + +```scala +val x18 = (name, "18_send_unpack_iall", cpg.call(".*SEND_UNPACK.*").location.toJson); +``` + +| discovery method | expected accuracy | +|--------------------|---------------------| +| joern | Perfect | + +
+ +
+ + +### Measurement + + +| Tool | Comm_1 | Comm_2 | Ground Truth | +|-------------|----------|----------|----------------| +| 17 May 2023 | yes | no | yes | + +
+ +
+ + + +
+ + +## 3 Instance + + +This instance shows the opposite and does not use the unpack operator on the `add` function. + +### Code + +```PHP + + +More + +
+ + +### Compile + + +```bash +$_main: + ; (lines=10, args=0, vars=2, tmps=5) + ; (before optimizer) + ; /.../PHP/18_send_unpack/3_instance_18_send_unpack/3_instance_18_send_unpack.php:1-9 + ; return [] RANGE[0..0] +0000 T2 = FETCH_R (global) string("_GET") +0001 T3 = FETCH_DIM_R T2 string("p1") +0002 ASSIGN CV0($b) T3 +0003 INIT_FCALL 2 112 string("add") +0004 SEND_VAL int(1) 1 +0005 SEND_VAR CV0($b) 2 +0006 V5 = DO_UCALL +0007 ASSIGN CV1($c) V5 +0008 ECHO CV1($c) +0009 RETURN int(1) + +add: + ; (lines=4, args=2, vars=2, tmps=0) + ; (before optimizer) + ; /.../PHP/18_send_unpack/3_instance_18_send_unpack/3_instance_18_send_unpack.php:3-5 + ; return [] RANGE[0..0] +0000 CV0($a) = RECV 1 +0001 CV1($b) = RECV 2 +0002 RETURN CV1($b) +0003 RETURN null +``` + +
+ +
+ + +### Discovery + + +This pattern does not use the unpack operator, so this rule will never be successfull + +```scala +val x18 = (name, "18_send_unpack_iall", cpg.call(".*SEND_UNPACK.*").location.toJson); +``` + +| discovery method | expected accuracy | +|--------------------|---------------------| +| joern | Perfect | + +
+ +
+ + +### Measurement + + +| Tool | Comm_1 | Comm_2 | Ground Truth | +|-------------|----------|----------|----------------| +| 17 May 2023 | yes | yes | yes | + +
+ +
+ + diff --git a/PHP/18_send_unpack/docs/description.md b/PHP/18_send_unpack/docs/description.md new file mode 100755 index 0000000..5d9f307 --- /dev/null +++ b/PHP/18_send_unpack/docs/description.md @@ -0,0 +1 @@ +The ellipsis (...) operator in PHP allows you to expand an array or iterable object into a list of arguments that can than be passed to a function. \ No newline at end of file diff --git a/PHP/18_send_unpack/docs/discovery_notes.md b/PHP/18_send_unpack/docs/discovery_notes.md new file mode 100755 index 0000000..73908b1 --- /dev/null +++ b/PHP/18_send_unpack/docs/discovery_notes.md @@ -0,0 +1 @@ +The unpack operator has an equivalent opcode instruction `SEND_UNPACK`. This rule searches for that. \ No newline at end of file diff --git a/PHP/19_closures/19_closures.json b/PHP/19_closures/19_closures.json old mode 100644 new mode 100755 index a248e64..353eab1 --- a/PHP/19_closures/19_closures.json +++ b/PHP/19_closures/19_closures.json @@ -1,15 +1,15 @@ -{ - "name": "Closures", - "description": "", - "family": "code_pattern_php", - "tags": [ - "sast", - "php", - "php_v7.4.9" - ], - "instances": [ - "./1_instance_19_closures/1_instance_19_closures.json", - "./2_instance_19_closures/2_instance_19_closures.json" - ], - "version": "v0.draft" +{ + "name": "Closures", + "description": "./docs/description.md", + "family": "code_pattern_php", + "tags": [ + "sast", + "php", + "php_v7.4.9" + ], + "instances": [ + "./1_instance_19_closures/1_instance_19_closures.json", + "./2_instance_19_closures/2_instance_19_closures.json" + ], + "version": "v1.0" } \ No newline at end of file diff --git a/PHP/19_closures/1_instance_19_closures/1_instance_19_closures.sc b/PHP/19_closures/19_closures.sc old mode 100644 new mode 100755 similarity index 97% rename from PHP/19_closures/1_instance_19_closures/1_instance_19_closures.sc rename to PHP/19_closures/19_closures.sc index 55899f8..29f5216 --- a/PHP/19_closures/1_instance_19_closures/1_instance_19_closures.sc +++ b/PHP/19_closures/19_closures.sc @@ -1,6 +1,6 @@ -@main def main(name : String): Unit = { - importCpg(name) - val x19 = (name, "19_closures_iall", cpg.call(".*DECLARE_LAMBDA_FUNCTION.*").location.toJson); - println(x19) - delete; +@main def main(name : String): Unit = { + importCpg(name) + val x19 = (name, "19_closures_iall", cpg.call(".*DECLARE_LAMBDA_FUNCTION.*").location.toJson); + println(x19) + delete; } \ No newline at end of file diff --git a/PHP/19_closures/1_instance_19_closures/1_instance_19_closures.bash b/PHP/19_closures/1_instance_19_closures/1_instance_19_closures.bash new file mode 100755 index 0000000..7a5b814 --- /dev/null +++ b/PHP/19_closures/1_instance_19_closures/1_instance_19_closures.bash @@ -0,0 +1,26 @@ + +$_main: + ; (lines=11, args=0, vars=3, tmps=7) + ; (before optimizer) + ; /.../PHP/19_closures/1_instance_19_closures/1_instance_19_closures.php:1-7 + ; return [] RANGE[0..0] +0000 T3 = DECLARE_LAMBDA_FUNCTION 0 +0001 ASSIGN CV0($greet) T3 +0002 T5 = FETCH_R (global) string("_GET") +0003 T6 = FETCH_DIM_R T5 string("p1") +0004 ASSIGN CV1($a) T6 +0005 INIT_DYNAMIC_CALL 1 CV0($greet) +0006 SEND_VAR_EX CV1($a) 1 +0007 V8 = DO_FCALL +0008 ASSIGN CV2($b) V8 +0009 ECHO CV2($b) +0010 RETURN int(1) + +{closure}: + ; (lines=3, args=1, vars=1, tmps=0) + ; (before optimizer) + ; /.../PHP/19_closures/1_instance_19_closures/1_instance_19_closures.php:2-4 + ; return [] RANGE[0..0] +0000 CV0($name) = RECV 1 +0001 RETURN CV0($name) +0002 RETURN null diff --git a/PHP/19_closures/1_instance_19_closures/1_instance_19_closures.json b/PHP/19_closures/1_instance_19_closures/1_instance_19_closures.json old mode 100644 new mode 100755 index c88386a..45e72df --- a/PHP/19_closures/1_instance_19_closures/1_instance_19_closures.json +++ b/PHP/19_closures/1_instance_19_closures/1_instance_19_closures.json @@ -1,37 +1,38 @@ -{ - "code": { - "path": "./1_instance_19_closures.php", - "injection_skeleton_broken": false - }, - "discovery": { - "rule": "./1_instance_19_closures.sc", - "method": "joern", - "rule_accuracy": "Perfect", - "notes": null - }, - "compile": { - "binary": null, - "instruction": null, - "dependencies": null - }, - "expectation": { - "type": "xss", - "sink_file": "./1_instance_19_closures.php", - "sink_line": 8, - "source_file": "./1_instance_19_closures.php", - "source_line": 6, - "expectation": false - }, - "properties": { - "category": "D2", - "feature_vs_internal_api": "FEATURE", - "input_sanitizer": false, - "source_and_sink": false, - "negative_test_case": true - }, - "remediation": { - "notes": "", - "transformation": null, - "modeling_rule": null - } +{ + "description": "This instance captures the declaration of an unnamed function.", + "code": { + "path": "./1_instance_19_closures.php", + "injection_skeleton_broken": false + }, + "discovery": { + "rule": "../19_closures.sc", + "method": "joern", + "rule_accuracy": "Perfect", + "notes": "Declaring unnamed functions has an opcode statement `DECLARE_LAMBDA_FUNCTION`, which this rule searches for." + }, + "compile": { + "binary": "./1_instance_19_closures.bash", + "instruction": null, + "dependencies": null + }, + "expectation": { + "type": "xss", + "sink_file": "./1_instance_19_closures.php", + "sink_line": 7, + "source_file": "./1_instance_19_closures.php", + "source_line": 5, + "expectation": true + }, + "properties": { + "category": "D2", + "feature_vs_internal_api": "FEATURE", + "input_sanitizer": false, + "source_and_sink": false, + "negative_test_case": true + }, + "remediation": { + "notes": "../docs/remediation_notes.md", + "transformation": null, + "modeling_rule": null + } } \ No newline at end of file diff --git a/PHP/19_closures/1_instance_19_closures/1_instance_19_closures.php b/PHP/19_closures/1_instance_19_closures/1_instance_19_closures.php old mode 100644 new mode 100755 index ad68ba8..d9f36d6 --- a/PHP/19_closures/1_instance_19_closures/1_instance_19_closures.php +++ b/PHP/19_closures/1_instance_19_closures/1_instance_19_closures.php @@ -1,8 +1,7 @@ - fn($y) => $x . $y; -echo $fn1("safe")($b); -?> \ No newline at end of file + fn($y) => $x . $y; +$a = $fn1("safe")($b); +echo a; // sink \ No newline at end of file diff --git a/PHP/19_closures/2_instance_19_closures/2_instance_19_closures.sc b/PHP/19_closures/2_instance_19_closures/2_instance_19_closures.sc deleted file mode 100644 index 55899f8..0000000 --- a/PHP/19_closures/2_instance_19_closures/2_instance_19_closures.sc +++ /dev/null @@ -1,6 +0,0 @@ -@main def main(name : String): Unit = { - importCpg(name) - val x19 = (name, "19_closures_iall", cpg.call(".*DECLARE_LAMBDA_FUNCTION.*").location.toJson); - println(x19) - delete; -} \ No newline at end of file diff --git a/PHP/19_closures/README.md b/PHP/19_closures/README.md old mode 100644 new mode 100755 index 240811e..c03d03d --- a/PHP/19_closures/README.md +++ b/PHP/19_closures/README.md @@ -1,183 +1,263 @@ -# Pattern: Closures - -## Category - -Functions - -## Definition - -## Instances - -### Instance 1 - -- CATEGORY: D2 -- FEATURE vs INTERNAL API: FEATURE -- INPUT SANITIZERS: NO -- SOURCES AND SINKS: NO -- NEGATIVE TEST CASES: NO -- CODE: - -```php - fn($y) => $x . $y; - -echo $fn1("safe")($b); -?> -``` - -- MEASUREMENT: - -| Tool | RIPS | phpSAFE | WAP | Progpilot | Comm_1 | Comm_2 | Correct | -| --------------- | ---- | ------- | ---- | --------- | ------- | --------- | ------- | -| VulnerabilityNO | YES | NO | NO | NO | NO | YES | YES | -Measurements Date: 8 June 2021 - -- OPCODE: - -```bash -$_main: ; (lines=17, args=0, vars=3, tmps=7) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/13_closures/second_ex/second_ex.php:1-8 -L0 (2): EXT_STMT -L1 (2): T3 = FETCH_R (global) string("_GET") -L2 (2): T4 = FETCH_DIM_R T3 string("p1") -L3 (2): ASSIGN CV0($b) T4 -L4 (5): EXT_STMT -L5 (5): T6 = DECLARE_LAMBDA_FUNCTION string("") -L6 (5): BIND_LEXICAL T6 CV2($y) -L7 (5): ASSIGN CV1($fn1) T6 -L8 (7): EXT_STMT -L9 (7): INIT_DYNAMIC_CALL 1 CV1($fn1) -L10 (7): SEND_VAL_EX string("safe") 1 -L11 (7): V8 = DO_FCALL -L12 (7): INIT_DYNAMIC_CALL 1 V8 -L13 (7): SEND_VAR_EX CV0($b) 1 -L14 (7): V9 = DO_FCALL -L15 (7): ECHO V9 -L16 (8): RETURN int(1) -LIVE RANGES: - 6: L6 - L7 (tmp/var) - -{closure}: ; (lines=9, args=1, vars=2, tmps=1) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/13_closures/second_ex/second_ex.php:5-5 -L0 (5): EXT_NOP -L1 (5): CV0($x) = RECV 1 -L2 (5): BIND_STATIC CV1($y) -L3 (5): EXT_STMT -L4 (5): T2 = DECLARE_LAMBDA_FUNCTION string("") -L5 (5): BIND_LEXICAL T2 CV0($x) -L6 (5): RETURN T2 -L7 (5): EXT_STMT -L8 (5): RETURN null -LIVE RANGES: - 2: L5 - L6 (tmp/var) - -{closure}: ; (lines=8, args=1, vars=2, tmps=1) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/13_closures/second_ex/second_ex.php:5-5 -L0 (5): EXT_NOP -L1 (5): CV0($y) = RECV 1 -L2 (5): BIND_STATIC CV1($x) -L3 (5): EXT_STMT -L4 (5): T2 = CONCAT CV1($x) CV0($y) -L5 (5): RETURN T2 -L6 (5): EXT_STMT -L7 (5): RETURN null -``` - -- DISCOVERY: - -```bash -cpg.call(".*DECLARE_LAMBDA_FUNCTION.*").location.l -``` - - -- PRECONDITIONS: - 1. -- TRANSFORMATION: - -``` - -``` - - - +[//]: # (This file is automatically generated. If you wish to make any changes, please use the JSON files and regenerate this file using the tpframework.) + +# Closures + +Tags: sast, php, php_v7.4.9 + +Version: v1.0 + +## Description + +[Closures or anonymous functions](https://www.php.net/manual/en/functions.anonymous.php) allow the creation of functions, that have no specific name. In PHP, a closure does not inherit the scope it was defined in. It rather encapsulates its scope and any variable that you want to access must be bind to the function manually. + +Instance 2 targets the usage of [arrow functions](https://www.php.net/manual/en/functions.arrow.php). In PHP the implementation of arrow function uses the closure class as well, they both have the same features. Except arrow functions capture variables from the parent scope by value automatically. + +## Overview + +| Instances | has discovery rule | discovery method | rule successfull | +|---------------------------|----------------------|--------------------|--------------------| +| [1 Instance](#1-instance) | yes | joern | yes | +| [2 Instance](#2-instance) | yes | joern | error | + +
+ + +## 1 Instance + + +This instance captures the declaration of an unnamed function. + +### Code + +```PHP + + +More + +
+ + +### Compile + + +```bash +$_main: + ; (lines=11, args=0, vars=3, tmps=7) + ; (before optimizer) + ; /.../PHP/19_closures/1_instance_19_closures/1_instance_19_closures.php:1-7 + ; return [] RANGE[0..0] +0000 T3 = DECLARE_LAMBDA_FUNCTION 0 +0001 ASSIGN CV0($greet) T3 +0002 T5 = FETCH_R (global) string("_GET") +0003 T6 = FETCH_DIM_R T5 string("p1") +0004 ASSIGN CV1($a) T6 +0005 INIT_DYNAMIC_CALL 1 CV0($greet) +0006 SEND_VAR_EX CV1($a) 1 +0007 V8 = DO_FCALL +0008 ASSIGN CV2($b) V8 +0009 ECHO CV2($b) +0010 RETURN int(1) + +{closure}: + ; (lines=3, args=1, vars=1, tmps=0) + ; (before optimizer) + ; /.../PHP/19_closures/1_instance_19_closures/1_instance_19_closures.php:2-4 + ; return [] RANGE[0..0] +0000 CV0($name) = RECV 1 +0001 RETURN CV0($name) +0002 RETURN null +``` + +
+ +
+ + +### Discovery + + +Declaring unnamed functions has an opcode statement `DECLARE_LAMBDA_FUNCTION`, which this rule searches for. + +```scala +val x19 = (name, "19_closures_iall", cpg.call(".*DECLARE_LAMBDA_FUNCTION.*").location.toJson); +``` + +| discovery method | expected accuracy | +|--------------------|---------------------| +| joern | Perfect | + +
+ +
+ + +### Measurement + + +| Tool | Comm_1 | Comm_2 | phpSAFE | Progpilot | RIPS | WAP | Ground Truth | +|-------------|----------|----------|-----------|-------------|--------|-------|----------------| +| 08 Jun 2021 | yes | yes | no | no | no | no | yes | +| 17 May 2023 | no | no | | | | | yes | + +
+ +
+ + +### Remediation + + +When it is easier for a SAST tool, to capture named functions, can one just rewrite an unnamed function and name it? + +
+ +
+ + + +
+ + +## 2 Instance + + +This instance captures another possibility of declaring an unnamed function in php, using arrow functions. + +### Code + +```PHP + fn($y) => $x . $y; +$a = $fn1("safe")($b); +echo a; // sink +``` + +### Instance Properties + +| category | feature_vs_internal_api | input_sanitizer | negative_test_case | source_and_sink | +|------------|---------------------------|-------------------|----------------------|-------------------| +| D2 | FEATURE | no | no | no | + +
+ +More + +
+ + +### Compile + + +```bash +$_main: + ; (lines=16, args=0, vars=4, tmps=9) + ; (before optimizer) + ; /.../PHP/19_closures/2_instance_19_closures/2_instance_19_closures.php:1-5 + ; return [] RANGE[0..0] +0000 T4 = FETCH_R (global) string("_GET") +0001 T5 = FETCH_DIM_R T4 string("p1") +0002 ASSIGN CV0($b) T5 +0003 T7 = DECLARE_LAMBDA_FUNCTION 0 +0004 BIND_LEXICAL T7 CV2($y) +0005 ASSIGN CV1($fn1) T7 +0006 INIT_DYNAMIC_CALL 1 CV1($fn1) +0007 SEND_VAL_EX string("safe") 1 +0008 V9 = DO_FCALL +0009 INIT_DYNAMIC_CALL 1 V9 +0010 SEND_VAR_EX CV0($b) 1 +0011 V10 = DO_FCALL +0012 ASSIGN CV3($a) V10 +0013 T12 = FETCH_CONSTANT string("a") +0014 ECHO T12 +0015 RETURN int(1) +LIVE RANGES: + 7: 0004 - 0005 (tmp/var) + +{closure}: + ; (lines=6, args=1, vars=2, tmps=1) + ; (before optimizer) + ; /.../PHP/19_closures/2_instance_19_closures/2_instance_19_closures.php:3-3 + ; return [] RANGE[0..0] +0000 CV0($x) = RECV 1 +0001 BIND_STATIC CV1($y) +0002 T2 = DECLARE_LAMBDA_FUNCTION 0 +0003 BIND_LEXICAL T2 CV0($x) +0004 RETURN T2 +0005 RETURN null +LIVE RANGES: + 2: 0003 - 0004 (tmp/var) + +{closure}: + ; (lines=5, args=1, vars=2, tmps=1) + ; (before optimizer) + ; /.../PHP/19_closures/2_instance_19_closures/2_instance_19_closures.php:3-3 + ; return [] RANGE[0..0] +0000 CV0($y) = RECV 1 +0001 BIND_STATIC CV1($x) +0002 T2 = CONCAT CV1($x) CV0($y) +0003 RETURN T2 +0004 RETURN null +``` + +
+ +
+ + +### Discovery + + +In May 2023, this instance throws an error while trying to generate the CPG using Joern. + +```scala +val x19 = (name, "19_closures_iall", cpg.call(".*DECLARE_LAMBDA_FUNCTION.*").location.toJson); +``` + +| discovery method | expected accuracy | +|--------------------|---------------------| +| joern | Perfect | + +
+ +
+ + +### Measurement + + +| Tool | Comm_1 | Comm_2 | phpSAFE | Progpilot | RIPS | WAP | Ground Truth | +|-------------|----------|----------|-----------|-------------|--------|-------|----------------| +| 08 Jun 2021 | no | yes | no | no | yes | no | yes | +| 17 May 2023 | no | yes | | | | | yes | + +
+ +
+ + +### Remediation + + +When it is easier for a SAST tool, to capture named functions, can one just rewrite an unnamed function and name it? + +
+ +
+ +
diff --git a/PHP/19_closures/docs/description.md b/PHP/19_closures/docs/description.md new file mode 100755 index 0000000..a80e3fd --- /dev/null +++ b/PHP/19_closures/docs/description.md @@ -0,0 +1,3 @@ +[Closures or anonymous functions](https://www.php.net/manual/en/functions.anonymous.php) allow the creation of functions, that have no specific name. In PHP, a closure does not inherit the scope it was defined in. It rather encapsulates its scope and any variable that you want to access must be bind to the function manually. + +Instance 2 targets the usage of [arrow functions](https://www.php.net/manual/en/functions.arrow.php). In PHP the implementation of arrow function uses the closure class as well, they both have the same features. Except arrow functions capture variables from the parent scope by value automatically. diff --git a/PHP/19_closures/docs/remediation_notes.md b/PHP/19_closures/docs/remediation_notes.md new file mode 100755 index 0000000..fcc1af7 --- /dev/null +++ b/PHP/19_closures/docs/remediation_notes.md @@ -0,0 +1 @@ +When it is easier for a SAST tool, to capture named functions, can one just rewrite an unnamed function and name it? \ No newline at end of file diff --git a/PHP/1_static_variables/1_instance_1_static_variables/1_instance_1_static_variables.bash b/PHP/1_static_variables/1_instance_1_static_variables/1_instance_1_static_variables.bash old mode 100644 new mode 100755 index d5d90d5..955f386 --- a/PHP/1_static_variables/1_instance_1_static_variables/1_instance_1_static_variables.bash +++ b/PHP/1_static_variables/1_instance_1_static_variables/1_instance_1_static_variables.bash @@ -1,31 +1,27 @@ - -$_main: ; (lines=13, args=0, vars=1, tmps=5) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/1_static_variables/1_static_variables.php:1-11 -L0 (9): EXT_STMT -L1 (9): T1 = FETCH_R (global) string("_GET") -L2 (9): T2 = FETCH_DIM_R T1 string("p1") -L3 (9): ASSIGN CV0($a) T2 -L4 (10): EXT_STMT -L5 (10): INIT_FCALL 1 128 string("f") -L6 (10): SEND_VAR CV0($a) 1 -L7 (10): DO_FCALL -L8 (11): EXT_STMT -L9 (11): INIT_FCALL 1 128 string("f") -L10 (11): SEND_VAL string("abc") 1 -L11 (11): DO_FCALL -L12 (11): RETURN int(1) - -F: ; (lines=10, args=1, vars=2, tmps=1) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/1_static_variables/1_static_variables.php:3-7 -L0 (3): EXT_NOP -L1 (3): CV0($a) = RECV 1 -L2 (4): EXT_STMT -L3 (4): BIND_STATIC (ref) CV1($b) -L4 (5): EXT_STMT -L5 (5): ECHO CV1($b) -L6 (6): EXT_STMT -L7 (6): ASSIGN CV1($b) CV0($a) -L8 (7): EXT_STMT -L9 (7): RETURN null \ No newline at end of file + +$_main: + ; (lines=10, args=0, vars=1, tmps=5) + ; (before optimizer) + ; /.../PHP/1_static_variables/1_instance_1_static_variables/1_instance_1_static_variables.php:1-11 + ; return [] RANGE[0..0] +0000 T1 = FETCH_R (global) string("_GET") +0001 T2 = FETCH_DIM_R T1 string("p1") +0002 ASSIGN CV0($a) T2 +0003 INIT_FCALL 1 128 string("f") +0004 SEND_VAR CV0($a) 1 +0005 DO_UCALL +0006 INIT_FCALL 1 128 string("f") +0007 SEND_VAL string("abc") 1 +0008 DO_UCALL +0009 RETURN int(1) + +F: + ; (lines=5, args=1, vars=2, tmps=1) + ; (before optimizer) + ; /.../PHP/1_static_variables/1_instance_1_static_variables/1_instance_1_static_variables.php:3-7 + ; return [] RANGE[0..0] +0000 CV0($a) = RECV 1 +0001 BIND_STATIC (ref) CV1($b) +0002 ECHO CV1($b) +0003 ASSIGN CV1($b) CV0($a) +0004 RETURN null diff --git a/PHP/1_static_variables/1_instance_1_static_variables/1_instance_1_static_variables.json b/PHP/1_static_variables/1_instance_1_static_variables/1_instance_1_static_variables.json old mode 100644 new mode 100755 index 71ac204..6d28dfb --- a/PHP/1_static_variables/1_instance_1_static_variables/1_instance_1_static_variables.json +++ b/PHP/1_static_variables/1_instance_1_static_variables/1_instance_1_static_variables.json @@ -1,37 +1,38 @@ -{ - "code": { - "path": "./1_instance_1_static_variables.php", - "injection_skeleton_broken": true - }, - "discovery": { - "rule": "./1_instance_1_static_variables.sc", - "method": "joern", - "rule_accuracy": "Perfect", - "notes": "The `BIND_STATIC` opcode is only for static variables that are normally used inside code blocks. The SAST tools may not able to keep the proper values for these static variables. As such the discovery rule should be accurate as it is" - }, - "remediation": { - "notes": "./docs/remediation_notes.md", - "transformation": null, - "modeling_rule": null - }, - "compile": { - "binary": "./1_instance_1_static_variables.bash", - "dependencies": null, - "instruction": null - }, - "expectation": { - "type": "xss", - "sink_file": "./1_instance_1_static_variables.php", - "sink_line": 5, - "source_file": "./1_instance_1_static_variables.php", - "source_line": 9, - "expectation": true - }, - "properties": { - "category": "S0", - "feature_vs_internal_api": "FEATURE", - "input_sanitizer": false, - "source_and_sink": false, - "negative_test_case": false - } +{ + "description": "This instance, focuses on the static variables in functions not the static properties in objects nor static methods.", + "code": { + "path": "./1_instance_1_static_variables.php", + "injection_skeleton_broken": true + }, + "discovery": { + "rule": "./1_instance_1_static_variables.sc", + "method": "joern", + "rule_accuracy": "Perfect", + "notes": "./docs/discovery_notes.md" + }, + "remediation": { + "notes": "./docs/remediation_notes.md", + "transformation": null, + "modeling_rule": null + }, + "compile": { + "binary": "./1_instance_1_static_variables.bash", + "dependencies": null, + "instruction": null + }, + "expectation": { + "type": "xss", + "sink_file": "./1_instance_1_static_variables.php", + "sink_line": 5, + "source_file": "./1_instance_1_static_variables.php", + "source_line": 9, + "expectation": true + }, + "properties": { + "category": "S0", + "feature_vs_internal_api": "FEATURE", + "input_sanitizer": false, + "source_and_sink": false, + "negative_test_case": false + } } \ No newline at end of file diff --git a/PHP/1_static_variables/1_instance_1_static_variables/1_instance_1_static_variables.php b/PHP/1_static_variables/1_instance_1_static_variables/1_instance_1_static_variables.php old mode 100644 new mode 100755 index e8f9a9f..34aaa43 --- a/PHP/1_static_variables/1_instance_1_static_variables/1_instance_1_static_variables.php +++ b/PHP/1_static_variables/1_instance_1_static_variables/1_instance_1_static_variables.php @@ -1,11 +1,11 @@ - + +More + +
+ + +### Compile + + +```bash +$_main: + ; (lines=10, args=0, vars=1, tmps=5) + ; (before optimizer) + ; /.../PHP/1_static_variables/1_instance_1_static_variables/1_instance_1_static_variables.php:1-11 + ; return [] RANGE[0..0] +0000 T1 = FETCH_R (global) string("_GET") +0001 T2 = FETCH_DIM_R T1 string("p1") +0002 ASSIGN CV0($a) T2 +0003 INIT_FCALL 1 128 string("f") +0004 SEND_VAR CV0($a) 1 +0005 DO_UCALL +0006 INIT_FCALL 1 128 string("f") +0007 SEND_VAL string("abc") 1 +0008 DO_UCALL +0009 RETURN int(1) + +F: + ; (lines=5, args=1, vars=2, tmps=1) + ; (before optimizer) + ; /.../PHP/1_static_variables/1_instance_1_static_variables/1_instance_1_static_variables.php:3-7 + ; return [] RANGE[0..0] +0000 CV0($a) = RECV 1 +0001 BIND_STATIC (ref) CV1($b) +0002 ECHO CV1($b) +0003 ASSIGN CV1($b) CV0($a) +0004 RETURN null +``` + +
+ +
+ + +### Discovery + + +To discover the static variables in opcode, I search for the opcode BIND_STATIC. +The `BIND_STATIC` opcode is only for static variables that are normally used inside code blocks. The SAST tools may not able to keep the proper values for these static variables. +As such the discovery rule should be accurate as it is. + +```scala +val x1 = (name, "1_static_variables_iall", cpg.call(".*BIND_STATIC.*").location.toJson); +``` + +| discovery method | expected accuracy | +|--------------------|---------------------| +| joern | Perfect | + +
+ +
+ + +### Measurement + + +| Tool | Comm_1 | Comm_2 | phpSAFE | Progpilot | RIPS | WAP | Ground Truth | +|-------------|----------|----------|-----------|-------------|--------|-------|----------------| +| 08 Jun 2021 | no | no | no | no | no | no | yes | +| 17 May 2023 | no | no | | | | | yes | + +
+ +
+ + +### Remediation + + +Likely this tarpit should be solved at the SAST tool side. Transforming a static variable into a non-static one is unfeasible. It is unclear how to create a modeling rule for the static keyword. + +
+ + diff --git a/PHP/1_static_variables/docs/description.md b/PHP/1_static_variables/docs/description.md old mode 100644 new mode 100755 diff --git a/PHP/20_use_with_closures/1_instance_20_use_with_closures/1_instance_20_use_with_closures.bash b/PHP/20_use_with_closures/1_instance_20_use_with_closures/1_instance_20_use_with_closures.bash old mode 100644 new mode 100755 index 0c08886..aa34eaf --- a/PHP/20_use_with_closures/1_instance_20_use_with_closures/1_instance_20_use_with_closures.bash +++ b/PHP/20_use_with_closures/1_instance_20_use_with_closures/1_instance_20_use_with_closures.bash @@ -1,32 +1,30 @@ - -$_main: ; (lines=16, args=0, vars=3, tmps=8) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/14_use_with_closures/first_ex/first_ex.php:1-13 -L0 (2): EXT_STMT -L1 (2): T3 = FETCH_R (global) string("_GET") -L2 (2): T4 = FETCH_DIM_R T3 string("p1") -L3 (2): ASSIGN CV0($b) T4 -L4 (3): EXT_STMT -L5 (3): ASSIGN CV1($message) string("safe") -L6 (5): EXT_STMT -L7 (5): T7 = DECLARE_LAMBDA_FUNCTION string("") -L8 (5): BIND_LEXICAL T7 CV1($message) -L9 (10): ASSIGN CV2($example) T7 -L10 (11): EXT_STMT -L11 (11): ASSIGN CV1($message) CV0($b) -L12 (12): EXT_STMT -L13 (12): INIT_DYNAMIC_CALL 0 CV2($example) -L14 (12): DO_FCALL -L15 (13): RETURN int(1) -LIVE RANGES: - 7: L8 - L9 (tmp/var) - -{closure}: ; (lines=6, args=0, vars=1, tmps=0) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/14_use_with_closures/first_ex/first_ex.php:5-10 -L0 (5): EXT_NOP -L1 (5): BIND_STATIC CV0($message) -L2 (9): EXT_STMT -L3 (9): ECHO CV0($message) -L4 (10): EXT_STMT -L5 (10): RETURN null \ No newline at end of file + +$_main: + ; (lines=13, args=0, vars=4, tmps=9) + ; (before optimizer) + ; /.../PHP/20_use_with_closures/1_instance_20_use_with_closures/1_instance_20_use_with_closures.php:1-14 + ; return [] RANGE[0..0] +0000 T4 = FETCH_R (global) string("_GET") +0001 T5 = FETCH_DIM_R T4 string("p1") +0002 ASSIGN CV0($a) T5 +0003 ASSIGN CV1($message) string("safe") +0004 T8 = DECLARE_LAMBDA_FUNCTION 0 +0005 BIND_LEXICAL T8 CV1($message) +0006 ASSIGN CV2($example) T8 +0007 ASSIGN CV1($message) CV0($a) +0008 INIT_DYNAMIC_CALL 0 CV2($example) +0009 V11 = DO_FCALL +0010 ASSIGN CV3($b) V11 +0011 ECHO CV3($b) +0012 RETURN int(1) +LIVE RANGES: + 8: 0005 - 0006 (tmp/var) + +{closure}: + ; (lines=3, args=0, vars=1, tmps=0) + ; (before optimizer) + ; /.../PHP/20_use_with_closures/1_instance_20_use_with_closures/1_instance_20_use_with_closures.php:5-10 + ; return [] RANGE[0..0] +0000 BIND_STATIC CV0($message) +0001 RETURN CV0($message) +0002 RETURN null diff --git a/PHP/20_use_with_closures/1_instance_20_use_with_closures/1_instance_20_use_with_closures.json b/PHP/20_use_with_closures/1_instance_20_use_with_closures/1_instance_20_use_with_closures.json old mode 100644 new mode 100755 index 36197a2..4b3c391 --- a/PHP/20_use_with_closures/1_instance_20_use_with_closures/1_instance_20_use_with_closures.json +++ b/PHP/20_use_with_closures/1_instance_20_use_with_closures/1_instance_20_use_with_closures.json @@ -1,37 +1,38 @@ -{ - "code": { - "path": "./1_instance_20_use_with_closures.php", - "injection_skeleton_broken": false - }, - "discovery": { - "rule": "./1_instance_20_use_with_closures.sc", - "method": "joern", - "rule_accuracy": "FP", - "notes": "To be perfect this rule could check that the BIND_LEXICAL is not associated to the reference of the variable used." - }, - "compile": { - "binary": "./1_instance_20_use_with_closures.bash", - "instruction": null, - "dependencies": null - }, - "expectation": { - "type": "xss", - "sink_file": "./1_instance_20_use_with_closures.php", - "sink_line": 13, - "source_file": "./1_instance_20_use_with_closures.php", - "source_line": 2, - "expectation": false - }, - "properties": { - "category": "D2", - "feature_vs_internal_api": "FEATURE", - "input_sanitizer": false, - "source_and_sink": false, - "negative_test_case": true - }, - "remediation": { - "notes": "", - "transformation": null, - "modeling_rule": null - } +{ + "description": "This instance captures the use of the keyword `use` in closures without reference, so any changes made to the value captured by the closure will not affect the captured value within the closure. Therefor this instance is not vulnerable", + "code": { + "path": "./1_instance_20_use_with_closures.php", + "injection_skeleton_broken": false + }, + "discovery": { + "rule": "./1_instance_20_use_with_closures.sc", + "method": "joern", + "rule_accuracy": "FP", + "notes": "To be perfect this rule could check that the BIND_LEXICAL is not associated to the reference of the variable used." + }, + "compile": { + "binary": "./1_instance_20_use_with_closures.bash", + "instruction": null, + "dependencies": null + }, + "expectation": { + "type": "xss", + "sink_file": "./1_instance_20_use_with_closures.php", + "sink_line": 13, + "source_file": "./1_instance_20_use_with_closures.php", + "source_line": 2, + "expectation": false + }, + "properties": { + "category": "D2", + "feature_vs_internal_api": "FEATURE", + "input_sanitizer": false, + "source_and_sink": false, + "negative_test_case": true + }, + "remediation": { + "notes": "", + "transformation": null, + "modeling_rule": null + } } \ No newline at end of file diff --git a/PHP/20_use_with_closures/1_instance_20_use_with_closures/1_instance_20_use_with_closures.php b/PHP/20_use_with_closures/1_instance_20_use_with_closures/1_instance_20_use_with_closures.php old mode 100644 new mode 100755 index 66092c2..cd8474a --- a/PHP/20_use_with_closures/1_instance_20_use_with_closures/1_instance_20_use_with_closures.php +++ b/PHP/20_use_with_closures/1_instance_20_use_with_closures/1_instance_20_use_with_closures.php @@ -1,13 +1,13 @@ -Closures may also inherit variables from the parent scope. Any such variables must be passed to the use language construct. - -## Instances - -### Instance 1 - -- CATEGORY: D2 -- FEATURE vs INTERNAL API: FEATURE -- INPUT SANITIZERS: NO -- SOURCES AND SINKS: NO -- NEGATIVE TEST CASES: YES -- CODE: - -```php - + + +## 1 Instance + + +This instance captures the use of the keyword `use` in closures without reference, so any changes made to the value captured by the closure will not affect the captured value within the closure. Therefor this instance is not vulnerable + +### Code + +```PHP + + +More + +
+ + +### Compile + + +```bash +$_main: + ; (lines=13, args=0, vars=4, tmps=9) + ; (before optimizer) + ; /.../PHP/20_use_with_closures/1_instance_20_use_with_closures/1_instance_20_use_with_closures.php:1-14 + ; return [] RANGE[0..0] +0000 T4 = FETCH_R (global) string("_GET") +0001 T5 = FETCH_DIM_R T4 string("p1") +0002 ASSIGN CV0($a) T5 +0003 ASSIGN CV1($message) string("safe") +0004 T8 = DECLARE_LAMBDA_FUNCTION 0 +0005 BIND_LEXICAL T8 CV1($message) +0006 ASSIGN CV2($example) T8 +0007 ASSIGN CV1($message) CV0($a) +0008 INIT_DYNAMIC_CALL 0 CV2($example) +0009 V11 = DO_FCALL +0010 ASSIGN CV3($b) V11 +0011 ECHO CV3($b) +0012 RETURN int(1) +LIVE RANGES: + 8: 0005 - 0006 (tmp/var) + +{closure}: + ; (lines=3, args=0, vars=1, tmps=0) + ; (before optimizer) + ; /.../PHP/20_use_with_closures/1_instance_20_use_with_closures/1_instance_20_use_with_closures.php:5-10 + ; return [] RANGE[0..0] +0000 BIND_STATIC CV0($message) +0001 RETURN CV0($message) +0002 RETURN null +``` + +
+ +
+ + +### Discovery + + +To be perfect this rule could check that the BIND_LEXICAL is not associated to the reference of the variable used. + +```scala +val x20 = (name, "20_use_with_closures_i1", cpg.call(".*BIND_LEXICAL.*").location.toJson); +``` + +| discovery method | expected accuracy | +|--------------------|---------------------| +| joern | FP | + +
+ +
+ + +### Measurement + + +| Tool | Comm_1 | Comm_2 | Ground Truth | +|-------------|----------|----------|----------------| +| 08 Jun 2021 | no | yes | no | +| 17 May 2023 | no | yes | no | + +
+ + + + + +
+ + +## 2 Instance + + +This instance shows what happens, when the value captured by the `use` keyword is passed by reference. Any changes made to the value will also be reflected in the scope of the closure. Therefor, this instance is vulnerable + +### Code + +```PHP + + +More + +
+ + +### Compile + + +```bash +$_main: + ; (lines=13, args=0, vars=4, tmps=9) + ; (before optimizer) + ; /.../PHP/20_use_with_closures/2_instance_20_use_with_closures/2_instance_20_use_with_closures.php:1-13 + ; return [] RANGE[0..0] +0000 T4 = FETCH_R (global) string("_GET") +0001 T5 = FETCH_DIM_R T4 string("p1") +0002 ASSIGN CV0($a) T5 +0003 ASSIGN CV1($message) string("safe") +0004 T8 = DECLARE_LAMBDA_FUNCTION 0 +0005 BIND_LEXICAL (ref) T8 CV1($message) +0006 ASSIGN CV2($example) T8 +0007 ASSIGN CV1($message) CV0($a) +0008 INIT_DYNAMIC_CALL 0 CV2($example) +0009 V11 = DO_FCALL +0010 ASSIGN CV3($b) V11 +0011 ECHO CV3($b) +0012 RETURN int(1) +LIVE RANGES: + 8: 0005 - 0006 (tmp/var) + +{closure}: + ; (lines=3, args=0, vars=1, tmps=0) + ; (before optimizer) + ; /.../PHP/20_use_with_closures/2_instance_20_use_with_closures/2_instance_20_use_with_closures.php:5-9 + ; return [] RANGE[0..0] +0000 BIND_STATIC (ref) CV0($message) +0001 RETURN CV0($message) +0002 RETURN null +``` + +
+ +
+ + +### Discovery + + +To be perfect this rule could check that the BIND_LEXICAL is associated to the reference of the variable used. + +```scala +val x20 = (name, "20_use_with_closures_i2", cpg.call(".*BIND_LEXICAL.*").location.toJson); +``` + +| discovery method | expected accuracy | +|--------------------|---------------------| +| joern | FP | + +
+ +
+ + +### Measurement + + +| Tool | Comm_1 | Comm_2 | phpSAFE | Progpilot | RIPS | WAP | Ground Truth | +|-------------|----------|----------|-----------|-------------|--------|-------|----------------| +| 08 Jun 2021 | yes | no | no | no | no | no | yes | +| 17 May 2023 | yes | no | | | | | yes | + +
+ +
+ + diff --git a/PHP/20_use_with_closures/docs/description.md b/PHP/20_use_with_closures/docs/description.md new file mode 100755 index 0000000..674f412 --- /dev/null +++ b/PHP/20_use_with_closures/docs/description.md @@ -0,0 +1 @@ +In PHP `use` can be used in the definition of a [closure](https://www.php.net/manual/en/functions.anonymous.php). It allows for variables of the parent scope to be accessed within the body of the function. Variables that are bind to the function using the `use` keyword are passed by value. \ No newline at end of file diff --git a/PHP/21_simple_object/1_instance_21_simple_object/1_instance_21_simple_object.bash b/PHP/21_simple_object/1_instance_21_simple_object/1_instance_21_simple_object.bash old mode 100644 new mode 100755 index 0c13c40..b4f6e25 --- a/PHP/21_simple_object/1_instance_21_simple_object/1_instance_21_simple_object.bash +++ b/PHP/21_simple_object/1_instance_21_simple_object/1_instance_21_simple_object.bash @@ -1,42 +1,38 @@ - -$_main: ; (lines=15, args=0, vars=2, tmps=7) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/66_simple_object/66_simple_object.php:1-19 -L0 (4): NOP -L1 (16): EXT_STMT -L2 (16): T2 = FETCH_R (global) string("_GET") -L3 (16): T3 = FETCH_DIM_R T2 string("p1") -L4 (16): ASSIGN CV0($b) T3 -L5 (17): EXT_STMT -L6 (17): V5 = NEW 1 string("Test") -L7 (17): SEND_VAR_EX CV0($b) 1 -L8 (17): DO_FCALL -L9 (17): ASSIGN CV1($test) V5 -L10 (19): EXT_STMT -L11 (19): INIT_METHOD_CALL 0 CV1($test) string("getfoo") -L12 (19): V8 = DO_FCALL -L13 (19): ECHO V8 -L14 (19): RETURN int(1) -LIVE RANGES: - 5: L7 - L9 (new) - -Test::__construct: ; (lines=7, args=1, vars=1, tmps=1) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/66_simple_object/66_simple_object.php:6-9 -L0 (6): EXT_NOP -L1 (6): CV0($foo) = RECV 1 -L2 (8): EXT_STMT -L3 (8): ASSIGN_OBJ THIS string("foo") -L4 (8): OP_DATA CV0($foo) -L5 (9): EXT_STMT -L6 (9): RETURN null - -Test::getfoo: ; (lines=6, args=0, vars=0, tmps=1) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/66_simple_object/66_simple_object.php:11-13 -L0 (11): EXT_NOP -L1 (12): EXT_STMT -L2 (12): T0 = FETCH_OBJ_R THIS string("foo") -L3 (12): RETURN T0 -L4 (13): EXT_STMT -L5 (13): RETURN null + +$_main: + ; (lines=11, args=0, vars=2, tmps=7) + ; (before optimizer) + ; /.../PHP/21_simple_object/1_instance_21_simple_object/1_instance_21_simple_object.php:1-17 + ; return [] RANGE[0..0] +0000 T2 = FETCH_R (global) string("_GET") +0001 T3 = FETCH_DIM_R T2 string("p1") +0002 ASSIGN CV0($b) T3 +0003 V5 = NEW 1 string("Test") +0004 SEND_VAR_EX CV0($b) 1 +0005 DO_FCALL +0006 ASSIGN CV1($test) V5 +0007 INIT_METHOD_CALL 0 CV1($test) string("getfoo") +0008 V8 = DO_FCALL +0009 ECHO V8 +0010 RETURN int(1) +LIVE RANGES: + 5: 0004 - 0006 (new) + +Test::__construct: + ; (lines=4, args=1, vars=1, tmps=1) + ; (before optimizer) + ; /.../PHP/21_simple_object/1_instance_21_simple_object/1_instance_21_simple_object.php:5-7 + ; return [] RANGE[0..0] +0000 CV0($foo) = RECV 1 +0001 ASSIGN_OBJ THIS string("foo") +0002 OP_DATA CV0($foo) +0003 RETURN null + +Test::getfoo: + ; (lines=3, args=0, vars=0, tmps=1) + ; (before optimizer) + ; /.../PHP/21_simple_object/1_instance_21_simple_object/1_instance_21_simple_object.php:9-11 + ; return [] RANGE[0..0] +0000 T0 = FETCH_OBJ_R THIS string("foo") +0001 RETURN T0 +0002 RETURN null diff --git a/PHP/21_simple_object/1_instance_21_simple_object/1_instance_21_simple_object.json b/PHP/21_simple_object/1_instance_21_simple_object/1_instance_21_simple_object.json old mode 100644 new mode 100755 index 345b3a4..551e22d --- a/PHP/21_simple_object/1_instance_21_simple_object/1_instance_21_simple_object.json +++ b/PHP/21_simple_object/1_instance_21_simple_object/1_instance_21_simple_object.json @@ -1,37 +1,38 @@ -{ - "code": { - "path": "./1_instance_21_simple_object.php", - "injection_skeleton_broken": false - }, - "discovery": { - "rule": "./1_instance_21_simple_object.sc", - "method": "joern", - "rule_accuracy": null, - "notes": null - }, - "compile": { - "binary": "./1_instance_21_simple_object.bash", - "instruction": null, - "dependencies": null - }, - "expectation": { - "type": "xss", - "sink_file": "./1_instance_21_simple_object.php", - "sink_line": 19, - "source_file": "./1_instance_21_simple_object.php", - "source_line": 16, - "expectation": true - }, - "properties": { - "category": "S0", - "feature_vs_internal_api": "FEATURE", - "input_sanitizer": false, - "source_and_sink": false, - "negative_test_case": false - }, - "remediation": { - "notes": "", - "transformation": null, - "modeling_rule": null - } +{ + "description": "This instance targets the use of a simple object, where an argument is passed to the constructor of that object.", + "code": { + "path": "./1_instance_21_simple_object.php", + "injection_skeleton_broken": false + }, + "discovery": { + "rule": "./1_instance_21_simple_object.sc", + "method": "joern", + "rule_accuracy": "FP", + "notes": "The rule could be more accurate, if we could check if there was an argument passed to the constructor." + }, + "compile": { + "binary": "./1_instance_21_simple_object.bash", + "instruction": null, + "dependencies": null + }, + "expectation": { + "type": "xss", + "sink_file": "./1_instance_21_simple_object.php", + "sink_line": 17, + "source_file": "./1_instance_21_simple_object.php", + "source_line": 14, + "expectation": true + }, + "properties": { + "category": "S0", + "feature_vs_internal_api": "FEATURE", + "input_sanitizer": false, + "source_and_sink": false, + "negative_test_case": false + }, + "remediation": { + "notes": "", + "transformation": null, + "modeling_rule": null + } } \ No newline at end of file diff --git a/PHP/21_simple_object/1_instance_21_simple_object/1_instance_21_simple_object.php b/PHP/21_simple_object/1_instance_21_simple_object/1_instance_21_simple_object.php old mode 100644 new mode 100755 index 98f017e..886d3b7 --- a/PHP/21_simple_object/1_instance_21_simple_object/1_instance_21_simple_object.php +++ b/PHP/21_simple_object/1_instance_21_simple_object/1_instance_21_simple_object.php @@ -1,19 +1,17 @@ -foo = $foo; - } - - public function getfoo(){ - return $this->foo; - } -} - -$b = $_GET["p1"]; -$test = new Test($b); -// XSS vulnerability -echo $test->getfoo(); \ No newline at end of file +foo = $foo; + } + + public function getfoo() { + return $this->foo; + } +} + +$b = $_GET["p1"]; // source +$test = new Test($b); // tarpit +// XSS vulnerability +echo $test->getfoo(); // sink \ No newline at end of file diff --git a/PHP/21_simple_object/1_instance_21_simple_object/1_instance_21_simple_object.sc b/PHP/21_simple_object/1_instance_21_simple_object/1_instance_21_simple_object.sc old mode 100644 new mode 100755 index 94101f4..22de5f1 --- a/PHP/21_simple_object/1_instance_21_simple_object/1_instance_21_simple_object.sc +++ b/PHP/21_simple_object/1_instance_21_simple_object/1_instance_21_simple_object.sc @@ -1,6 +1,6 @@ -@main def main(name : String): Unit = { - importCpg(name) - val x21 = (name, "21_simple_object_iall", cpg.call(".*OBJ.*|.*NEW.*|.*CLONE.*|.*METHOD.*").location.toJson); - println(x21) - delete; +@main def main(name : String): Unit = { + importCpg(name) + val x21 = (name, "21_simple_object_iall", cpg.call(".*OBJ.*|.*NEW.*|.*CLONE.*|.*METHOD.*").location.toJson); + println(x21) + delete; } \ No newline at end of file diff --git a/PHP/21_simple_object/21_simple_object.json b/PHP/21_simple_object/21_simple_object.json old mode 100644 new mode 100755 index 5c5f1f1..0b6cf90 --- a/PHP/21_simple_object/21_simple_object.json +++ b/PHP/21_simple_object/21_simple_object.json @@ -1,14 +1,14 @@ -{ - "name": "Simple Object", - "description": "", - "family": "code_pattern_php", - "tags": [ - "sast", - "php", - "php_v7.4.9" - ], - "instances": [ - "./1_instance_21_simple_object/1_instance_21_simple_object.json" - ], - "version": "v0.draft" +{ + "name": "Simple Object", + "description": "./docs/description.md", + "family": "code_pattern_php", + "tags": [ + "sast", + "php", + "php_v7.4.9" + ], + "instances": [ + "./1_instance_21_simple_object/1_instance_21_simple_object.json" + ], + "version": "v1.0" } \ No newline at end of file diff --git a/PHP/21_simple_object/README.md b/PHP/21_simple_object/README.md old mode 100644 new mode 100755 index 99a4d81..8959ded --- a/PHP/21_simple_object/README.md +++ b/PHP/21_simple_object/README.md @@ -1,109 +1,134 @@ -# Pattern: Simple Object - -## Category - -Objects - -## Definition - -## Instances - -### Instance 1 - -- CATEGORY: S0 -- FEATURE vs INTERNAL API: FEATURE -- INPUT SANITIZERS: NO -- SOURCES AND SINKS: NO -- NEGATIVE TEST CASES: NO -- CODE: - -```php -foo = $foo; - } - - public function getfoo(){ - return $this->foo; - } -} - -$b = $_GET["p1"]; -$test = new Test($b); -// XSS vulnerability -echo $test->getfoo(); -``` - -- MEASUREMENT: - -| Tool | RIPS | phpSAFE | WAP | Progpilot | Comm_1 | Comm_2 | Correct | -| ------------- | ---- | ------- | ---- | --------- | ------- | --------- | ------- | -| Vulnerability | NO | NO | NO | YES | YES | YES | YES | -Measurements Date: 8 June 2021 - -- OPCODE: - -```bash -$_main: ; (lines=15, args=0, vars=2, tmps=7) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/66_simple_object/66_simple_object.php:1-19 -L0 (4): NOP -L1 (16): EXT_STMT -L2 (16): T2 = FETCH_R (global) string("_GET") -L3 (16): T3 = FETCH_DIM_R T2 string("p1") -L4 (16): ASSIGN CV0($b) T3 -L5 (17): EXT_STMT -L6 (17): V5 = NEW 1 string("Test") -L7 (17): SEND_VAR_EX CV0($b) 1 -L8 (17): DO_FCALL -L9 (17): ASSIGN CV1($test) V5 -L10 (19): EXT_STMT -L11 (19): INIT_METHOD_CALL 0 CV1($test) string("getfoo") -L12 (19): V8 = DO_FCALL -L13 (19): ECHO V8 -L14 (19): RETURN int(1) -LIVE RANGES: - 5: L7 - L9 (new) - -Test::__construct: ; (lines=7, args=1, vars=1, tmps=1) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/66_simple_object/66_simple_object.php:6-9 -L0 (6): EXT_NOP -L1 (6): CV0($foo) = RECV 1 -L2 (8): EXT_STMT -L3 (8): ASSIGN_OBJ THIS string("foo") -L4 (8): OP_DATA CV0($foo) -L5 (9): EXT_STMT -L6 (9): RETURN null - -Test::getfoo: ; (lines=6, args=0, vars=0, tmps=1) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/66_simple_object/66_simple_object.php:11-13 -L0 (11): EXT_NOP -L1 (12): EXT_STMT -L2 (12): T0 = FETCH_OBJ_R THIS string("foo") -L3 (12): RETURN T0 -L4 (13): EXT_STMT -L5 (13): RETURN null -``` - -- DISCOVERY: - -```bash -cpg.call(".*OBJ.*|.*NEW.*|.*CLONE.*|.*METHOD.*").location.l -``` - -- PRECONDITIONS: - 1. - -- TRANSFORMATION: - -``` - -``` - +[//]: # (This file is automatically generated. If you wish to make any changes, please use the JSON files and regenerate this file using the tpframework.) + +# Simple Object + +Tags: sast, php, php_v7.4.9 + +Version: v1.0 + +## Description + +This pattern should target the creation and usage of simple objects. + +## Overview + +| Instances | has discovery rule | discovery method | rule successfull | +|---------------------------|----------------------|--------------------|--------------------| +| [1 Instance](#1-instance) | yes | joern | yes | + +## 1 Instance + +This instance targets the use of a simple object, where an argument is passed to the constructor of that object. + +### Code + +```PHP +foo = $foo; + } + + public function getfoo() { + return $this->foo; + } +} + +$b = $_GET["p1"]; // source +$test = new Test($b); // tarpit +// XSS vulnerability +echo $test->getfoo(); // sink +``` + +### Instance Properties + +| category | feature_vs_internal_api | input_sanitizer | negative_test_case | source_and_sink | +|------------|---------------------------|-------------------|----------------------|-------------------| +| S0 | FEATURE | no | no | no | + +
+ +More + +
+ + +### Compile + + +```bash +$_main: + ; (lines=11, args=0, vars=2, tmps=7) + ; (before optimizer) + ; /.../PHP/21_simple_object/1_instance_21_simple_object/1_instance_21_simple_object.php:1-17 + ; return [] RANGE[0..0] +0000 T2 = FETCH_R (global) string("_GET") +0001 T3 = FETCH_DIM_R T2 string("p1") +0002 ASSIGN CV0($b) T3 +0003 V5 = NEW 1 string("Test") +0004 SEND_VAR_EX CV0($b) 1 +0005 DO_FCALL +0006 ASSIGN CV1($test) V5 +0007 INIT_METHOD_CALL 0 CV1($test) string("getfoo") +0008 V8 = DO_FCALL +0009 ECHO V8 +0010 RETURN int(1) +LIVE RANGES: + 5: 0004 - 0006 (new) + +Test::__construct: + ; (lines=4, args=1, vars=1, tmps=1) + ; (before optimizer) + ; /.../PHP/21_simple_object/1_instance_21_simple_object/1_instance_21_simple_object.php:5-7 + ; return [] RANGE[0..0] +0000 CV0($foo) = RECV 1 +0001 ASSIGN_OBJ THIS string("foo") +0002 OP_DATA CV0($foo) +0003 RETURN null + +Test::getfoo: + ; (lines=3, args=0, vars=0, tmps=1) + ; (before optimizer) + ; /.../PHP/21_simple_object/1_instance_21_simple_object/1_instance_21_simple_object.php:9-11 + ; return [] RANGE[0..0] +0000 T0 = FETCH_OBJ_R THIS string("foo") +0001 RETURN T0 +0002 RETURN null +``` + +
+ +
+ + +### Discovery + + +The rule could be more accurate, if we could check if there was an argument passed to the constructor. + +```scala +val x21 = (name, "21_simple_object_iall", cpg.call(".*OBJ.*|.*NEW.*|.*CLONE.*|.*METHOD.*").location.toJson); +``` + +| discovery method | expected accuracy | +|--------------------|---------------------| +| joern | FP | + +
+ +
+ + +### Measurement + + +| Tool | Comm_1 | Comm_2 | phpSAFE | Progpilot | RIPS | WAP | Ground Truth | +|-------------|----------|----------|-----------|-------------|--------|-------|----------------| +| 08 Jun 2021 | yes | yes | no | yes | no | no | yes | +| 17 May 2023 | yes | yes | | | | | yes | + +
+ +
diff --git a/PHP/21_simple_object/docs/description.md b/PHP/21_simple_object/docs/description.md new file mode 100755 index 0000000..700d543 --- /dev/null +++ b/PHP/21_simple_object/docs/description.md @@ -0,0 +1 @@ +This pattern should target the creation and usage of simple objects. diff --git a/PHP/22_assign_object/1_instance_22_assign_object/1_instance_22_assign_object.bash b/PHP/22_assign_object/1_instance_22_assign_object/1_instance_22_assign_object.bash old mode 100644 new mode 100755 index a970ae3..25d203a --- a/PHP/22_assign_object/1_instance_22_assign_object/1_instance_22_assign_object.bash +++ b/PHP/22_assign_object/1_instance_22_assign_object/1_instance_22_assign_object.bash @@ -1,25 +1,21 @@ - -$_main: ; (lines=19, args=0, vars=2, tmps=9) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/24_assign_object/24_assign_object.php:1-10 -L0 (3): NOP -L1 (5): EXT_STMT -L2 (5): V2 = NEW 0 string("myclass") -L3 (5): DO_FCALL -L4 (5): ASSIGN CV0($obj1) V2 -L5 (6): EXT_STMT -L6 (6): ASSIGN_OBJ CV0($obj1) string("b") -L7 (6): OP_DATA string("safe2") -L8 (7): EXT_STMT -L9 (7): ASSIGN CV1($obj2) CV0($obj1) -L10 (8): EXT_STMT -L11 (8): T8 = FETCH_R (global) string("_GET") -L12 (8): T9 = FETCH_DIM_R T8 string("p1") -L13 (8): ASSIGN_OBJ CV1($obj2) string("b") -L14 (8): OP_DATA T9 -L15 (10): EXT_STMT -L16 (10): T10 = FETCH_OBJ_R CV0($obj1) string("b") -L17 (10): ECHO T10 -L18 (10): RETURN int(1) -LIVE RANGES: - 2: L3 - L4 (new) + +$_main: + ; (lines=13, args=0, vars=2, tmps=9) + ; (before optimizer) + ; /.../PHP/22_assign_object/1_instance_22_assign_object/1_instance_22_assign_object.php:1-10 + ; return [] RANGE[0..0] +0000 V2 = NEW 0 string("myclass") +0001 DO_FCALL +0002 ASSIGN CV0($obj1) V2 +0003 ASSIGN_OBJ CV0($obj1) string("b") +0004 OP_DATA string("safe2") +0005 ASSIGN CV1($obj2) CV0($obj1) +0006 T8 = FETCH_R (global) string("_GET") +0007 T9 = FETCH_DIM_R T8 string("p1") +0008 ASSIGN_OBJ CV1($obj2) string("b") +0009 OP_DATA T9 +0010 T10 = FETCH_OBJ_R CV0($obj1) string("b") +0011 ECHO T10 +0012 RETURN int(1) +LIVE RANGES: + 2: 0001 - 0002 (new) diff --git a/PHP/22_assign_object/1_instance_22_assign_object/1_instance_22_assign_object.json b/PHP/22_assign_object/1_instance_22_assign_object/1_instance_22_assign_object.json old mode 100644 new mode 100755 index 67e7db9..cdc8c41 --- a/PHP/22_assign_object/1_instance_22_assign_object/1_instance_22_assign_object.json +++ b/PHP/22_assign_object/1_instance_22_assign_object/1_instance_22_assign_object.json @@ -1,37 +1,38 @@ -{ - "code": { - "path": "./1_instance_22_assign_object.php", - "injection_skeleton_broken": false - }, - "discovery": { - "rule": "./1_instance_22_assign_object.sc", - "method": "joern", - "rule_accuracy": null, - "notes": null - }, - "compile": { - "binary": "./1_instance_22_assign_object.bash", - "instruction": null, - "dependencies": null - }, - "expectation": { - "type": "xss", - "sink_file": "./1_instance_22_assign_object.php", - "sink_line": 10, - "source_file": "./1_instance_22_assign_object.php", - "source_line": 8, - "expectation": true - }, - "properties": { - "category": "S0", - "feature_vs_internal_api": "FEATURE", - "input_sanitizer": false, - "source_and_sink": false, - "negative_test_case": false - }, - "remediation": { - "notes": "", - "transformation": null, - "modeling_rule": null - } +{ + "description": "This instance shows, then when assigning one object to another object, they both point to the same object in memory, so they have the same attributes.", + "code": { + "path": "./1_instance_22_assign_object.php", + "injection_skeleton_broken": false + }, + "discovery": { + "rule": "./1_instance_22_assign_object.sc", + "method": "joern", + "rule_accuracy": "Perfect", + "notes": "../docs/discovery_notes.md" + }, + "compile": { + "binary": "./1_instance_22_assign_object.bash", + "instruction": null, + "dependencies": null + }, + "expectation": { + "type": "xss", + "sink_file": "./1_instance_22_assign_object.php", + "sink_line": 10, + "source_file": "./1_instance_22_assign_object.php", + "source_line": 8, + "expectation": true + }, + "properties": { + "category": "S0", + "feature_vs_internal_api": "FEATURE", + "input_sanitizer": false, + "source_and_sink": false, + "negative_test_case": false + }, + "remediation": { + "notes": "", + "transformation": null, + "modeling_rule": null + } } \ No newline at end of file diff --git a/PHP/22_assign_object/1_instance_22_assign_object/1_instance_22_assign_object.php b/PHP/22_assign_object/1_instance_22_assign_object/1_instance_22_assign_object.php old mode 100644 new mode 100755 index ea287fc..6945785 --- a/PHP/22_assign_object/1_instance_22_assign_object/1_instance_22_assign_object.php +++ b/PHP/22_assign_object/1_instance_22_assign_object/1_instance_22_assign_object.php @@ -1,10 +1,10 @@ -b = "safe2"; -$obj2 = $obj1; -$obj2->b = $_GET["p1"]; -// XSS vulnerability -echo $obj1->b; \ No newline at end of file +b = "safe2"; +$obj2 = $obj1; +$obj2->b = $_GET["p1"]; // source +// XSS vulnerability +echo $obj1->b; // sink \ No newline at end of file diff --git a/PHP/22_assign_object/1_instance_22_assign_object/1_instance_22_assign_object.sc b/PHP/22_assign_object/1_instance_22_assign_object/1_instance_22_assign_object.sc old mode 100644 new mode 100755 index af9a4a3..a4f0d82 --- a/PHP/22_assign_object/1_instance_22_assign_object/1_instance_22_assign_object.sc +++ b/PHP/22_assign_object/1_instance_22_assign_object/1_instance_22_assign_object.sc @@ -1,8 +1,8 @@ -@main def main(name : String): Unit = { - importCpg(name) - val G1 = cpg.call("ASSIGN").reachableByFlows(cpg.call.code(".*NEW.*")).map(_.elements.last).collect{ case c : nodes.Call => c}.argument.order(0).isIdentifier.name.l.distinct - val R1 = cpg.call("ASSIGN").code(".*CV.*CV.*").l - val x22 = (name, "22_assign_object_iall", R1.filter{ call => G1.exists{ h => call.argument.order(1).code.l.contains("CV($" + h + ")")} }.location.toJson); - println(x22) - delete; -} \ No newline at end of file +@main def main(name : String): Unit = { + importCpg(name) + val G1 = cpg.call("ASSIGN").reachableByFlows(cpg.call.code(".*NEW.*")).map(_.elements.last).collect{ case c : nodes.Call => c}.argument.order(0).isIdentifier.name.l.distinct + val R1 = cpg.call("ASSIGN").code(".*CV.*CV.*").l + val x22 = (name, "22_assign_object_iall", R1.filter{ call => G1.exists{ h => call.argument.order(1).code.l.contains("CV($" + h + ")")} }.location.toJson); + println(x22) + delete; +} \ No newline at end of file diff --git a/PHP/22_assign_object/22_assign_object.json b/PHP/22_assign_object/22_assign_object.json old mode 100644 new mode 100755 index f31dcf8..8b107eb --- a/PHP/22_assign_object/22_assign_object.json +++ b/PHP/22_assign_object/22_assign_object.json @@ -1,14 +1,14 @@ -{ - "name": "Assign Object", - "description": "", - "family": "code_pattern_php", - "tags": [ - "sast", - "php", - "php_v7.4.9" - ], - "instances": [ - "./1_instance_22_assign_object/1_instance_22_assign_object.json" - ], - "version": "v0.draft" +{ + "name": "Assign Object", + "description": "./docs/description.md", + "family": "code_pattern_php", + "tags": [ + "sast", + "php", + "php_v7.4.9" + ], + "instances": [ + "./1_instance_22_assign_object/1_instance_22_assign_object.json" + ], + "version": "v1.0" } \ No newline at end of file diff --git a/PHP/22_assign_object/README.md b/PHP/22_assign_object/README.md old mode 100644 new mode 100755 index b0cf89b..d451bba --- a/PHP/22_assign_object/README.md +++ b/PHP/22_assign_object/README.md @@ -1,85 +1,114 @@ -# Pattern: Assign Object - -## Category - -Objects - -## Definition - -## Instances - -### Instance 1 - -- CATEGORY: S0 -- FEATURE vs INTERNAL API: FEATURE -- INPUT SANITIZERS: NO -- SOURCES AND SINKS: NO -- NEGATIVE TEST CASES: NO -- CODE: - -```php -b = "safe2"; -$obj2 = $obj1; -$obj2->b = $_GET["p1"]; -// XSS vulnerability -echo $obj1->b; -``` - -- MEASUREMENT: - -| Tool | RIPS | phpSAFE | WAP | Progpilot | Comm_1 | Comm_2 | Correct | -| ------------- | ---- | ------- | ---- | --------- | ------- | --------- | ------- | -| Vulnerability | NO | NO |YES | NO | YES | NO | YES | -Measurements Date: 8 June 2021 - -- OPCODE: - -```bash -$_main: ; (lines=19, args=0, vars=2, tmps=9) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/24_assign_object/24_assign_object.php:1-10 -L0 (3): NOP -L1 (5): EXT_STMT -L2 (5): V2 = NEW 0 string("myclass") -L3 (5): DO_FCALL -L4 (5): ASSIGN CV0($obj1) V2 -L5 (6): EXT_STMT -L6 (6): ASSIGN_OBJ CV0($obj1) string("b") -L7 (6): OP_DATA string("safe2") -L8 (7): EXT_STMT -L9 (7): ASSIGN CV1($obj2) CV0($obj1) -L10 (8): EXT_STMT -L11 (8): T8 = FETCH_R (global) string("_GET") -L12 (8): T9 = FETCH_DIM_R T8 string("p1") -L13 (8): ASSIGN_OBJ CV1($obj2) string("b") -L14 (8): OP_DATA T9 -L15 (10): EXT_STMT -L16 (10): T10 = FETCH_OBJ_R CV0($obj1) string("b") -L17 (10): ECHO T10 -L18 (10): RETURN int(1) -LIVE RANGES: - 2: L3 - L4 (new) -``` - -- DISCOVERY: - -```bash -val G1 = cpg.call("ASSIGN").reachableByFlows(cpg.call.code(".*NEW.*")).map(_.elements.last).collect{ case c : nodes.Call => c}.argument.order(0).isIdentifier.name.l.distinct -val R1 = cpg.call("ASSIGN").code(".*CV.*CV.*").l -R1.filter{ call => G1.exists{ h => call.argument.order(1).code.l.contains("CV($" + h + ")")} }.size; -``` - -- PRECONDITIONS: - 1. - -- TRANSFORMATION: - -``` - -``` - +[//]: # (This file is automatically generated. If you wish to make any changes, please use the JSON files and regenerate this file using the tpframework.) + +# Assign Object + +Tags: sast, php, php_v7.4.9 + +Version: v1.0 + +## Description + +Object assignment in PHP creates a reference to the same object in memory, rather than a new copy, which means that any changes made to the object through one variable or function parameter will be reflected in all other variables or parameters that reference the same object. + +## Overview + +| Instances | has discovery rule | discovery method | rule successfull | +|---------------------------|----------------------|--------------------|--------------------| +| [1 Instance](#1-instance) | yes | joern | yes | + +## 1 Instance + +This instance shows, then when assigning one object to another object, they both point to the same object in memory, so they have the same attributes. + +### Code + +```PHP +b = "safe2"; +$obj2 = $obj1; +$obj2->b = $_GET["p1"]; // source +// XSS vulnerability +echo $obj1->b; // sink +``` + +### Instance Properties + +| category | feature_vs_internal_api | input_sanitizer | negative_test_case | source_and_sink | +|------------|---------------------------|-------------------|----------------------|-------------------| +| S0 | FEATURE | no | no | no | + +
+ +More + +
+ + +### Compile + + +```bash +$_main: + ; (lines=13, args=0, vars=2, tmps=9) + ; (before optimizer) + ; /.../PHP/22_assign_object/1_instance_22_assign_object/1_instance_22_assign_object.php:1-10 + ; return [] RANGE[0..0] +0000 V2 = NEW 0 string("myclass") +0001 DO_FCALL +0002 ASSIGN CV0($obj1) V2 +0003 ASSIGN_OBJ CV0($obj1) string("b") +0004 OP_DATA string("safe2") +0005 ASSIGN CV1($obj2) CV0($obj1) +0006 T8 = FETCH_R (global) string("_GET") +0007 T9 = FETCH_DIM_R T8 string("p1") +0008 ASSIGN_OBJ CV1($obj2) string("b") +0009 OP_DATA T9 +0010 T10 = FETCH_OBJ_R CV0($obj1) string("b") +0011 ECHO T10 +0012 RETURN int(1) +LIVE RANGES: + 2: 0001 - 0002 (new) +``` + +
+ +
+ + +### Discovery + + +This rules first collects all `ASSIGN` calls, which are reachable by a `NEW` call in `G1`. +In `R1` it collects all `ASSIGN` calls where one variable is assigned to another variable in a list. +Finally, the rule filters out all `ASSIGN` calls from `R1`, where any call from `G1` is invoked in. + +```scala +val G1 = cpg.call("ASSIGN").reachableByFlows(cpg.call.code(".*NEW.*")).map(_.elements.last).collect{ case c : nodes.Call => c}.argument.order(0).isIdentifier.name.l.distinct +val R1 = cpg.call("ASSIGN").code(".*CV.*CV.*").l +val x22 = (name, "22_assign_object_iall", R1.filter{ call => G1.exists{ h => call.argument.order(1).code.l.contains("CV($" + h + ")")} }.location.toJson); +``` + +| discovery method | expected accuracy | +|--------------------|---------------------| +| joern | Perfect | + +
+ +
+ + +### Measurement + + +| Tool | Comm_1 | Comm_2 | phpSAFE | Progpilot | RIPS | WAP | Ground Truth | +|-------------|----------|----------|-----------|-------------|--------|-------|----------------| +| 08 Jun 2021 | yes | no | no | no | no | yes | yes | +| 17 May 2023 | yes | no | | | | | yes | + +
+ +
diff --git a/PHP/22_assign_object/docs/description.md b/PHP/22_assign_object/docs/description.md new file mode 100755 index 0000000..ed4e3ca --- /dev/null +++ b/PHP/22_assign_object/docs/description.md @@ -0,0 +1 @@ +Object assignment in PHP creates a reference to the same object in memory, rather than a new copy, which means that any changes made to the object through one variable or function parameter will be reflected in all other variables or parameters that reference the same object. \ No newline at end of file diff --git a/PHP/22_assign_object/docs/discovery_notes.md b/PHP/22_assign_object/docs/discovery_notes.md new file mode 100755 index 0000000..ba9d38f --- /dev/null +++ b/PHP/22_assign_object/docs/discovery_notes.md @@ -0,0 +1,3 @@ +This rules first collects all `ASSIGN` calls, which are reachable by a `NEW` call in `G1`. +In `R1` it collects all `ASSIGN` calls where one variable is assigned to another variable in a list. +Finally, the rule filters out all `ASSIGN` calls from `R1`, where any call from `G1` is invoked in. \ No newline at end of file diff --git a/PHP/23_object_argument/1_instance_23_object_argument/1_instance_23_object_argument.bash b/PHP/23_object_argument/1_instance_23_object_argument/1_instance_23_object_argument.bash old mode 100644 new mode 100755 index a5acf70..372a46b --- a/PHP/23_object_argument/1_instance_23_object_argument/1_instance_23_object_argument.bash +++ b/PHP/23_object_argument/1_instance_23_object_argument/1_instance_23_object_argument.bash @@ -1,38 +1,34 @@ - -$_main: ; (lines=20, args=0, vars=2, tmps=9) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/25_object_argument/25_object_argument.php:1-13 -L0 (2): EXT_STMT -L1 (2): V2 = NEW 0 string("stdClass") -L2 (2): DO_FCALL -L3 (2): ASSIGN CV0($x) V2 -L4 (3): EXT_STMT -L5 (3): ASSIGN_OBJ CV0($x) string("prop") -L6 (3): OP_DATA string("abc") -L7 (10): EXT_STMT -L8 (10): T6 = FETCH_R (global) string("_GET") -L9 (10): T7 = FETCH_DIM_R T6 string("p1") -L10 (10): ASSIGN CV1($b) T7 -L11 (11): EXT_STMT -L12 (11): INIT_FCALL 2 128 string("f") -L13 (11): SEND_VAR CV0($x) 1 -L14 (11): SEND_VAR CV1($b) 2 -L15 (11): DO_FCALL -L16 (13): EXT_STMT -L17 (13): T10 = FETCH_OBJ_R CV0($x) string("prop") -L18 (13): ECHO T10 -L19 (13): RETURN int(1) -LIVE RANGES: - 2: L2 - L3 (new) - -f: ; (lines=8, args=2, vars=2, tmps=1) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/25_object_argument/25_object_argument.php:6-8 -L0 (6): EXT_NOP -L1 (6): CV0($o) = RECV 1 -L2 (6): CV1($b) = RECV 2 -L3 (7): EXT_STMT -L4 (7): ASSIGN_OBJ CV0($o) string("prop") -L5 (7): OP_DATA CV1($b) -L6 (8): EXT_STMT -L7 (8): RETURN null + +$_main: + ; (lines=15, args=0, vars=2, tmps=9) + ; (before optimizer) + ; /.../PHP/23_object_argument/1_instance_23_object_argument/1_instance_23_object_argument.php:1-13 + ; return [] RANGE[0..0] +0000 V2 = NEW 0 string("stdClass") +0001 DO_FCALL +0002 ASSIGN CV0($x) V2 +0003 ASSIGN_OBJ CV0($x) string("prop") +0004 OP_DATA string("abc") +0005 T6 = FETCH_R (global) string("_GET") +0006 T7 = FETCH_DIM_R T6 string("p1") +0007 ASSIGN CV1($b) T7 +0008 INIT_FCALL 2 128 string("f") +0009 SEND_VAR CV0($x) 1 +0010 SEND_VAR CV1($b) 2 +0011 DO_UCALL +0012 T10 = FETCH_OBJ_R CV0($x) string("prop") +0013 ECHO T10 +0014 RETURN int(1) +LIVE RANGES: + 2: 0001 - 0002 (new) + +f: + ; (lines=5, args=2, vars=2, tmps=1) + ; (before optimizer) + ; /.../PHP/23_object_argument/1_instance_23_object_argument/1_instance_23_object_argument.php:6-8 + ; return [] RANGE[0..0] +0000 CV0($o) = RECV 1 +0001 CV1($b) = RECV 2 +0002 ASSIGN_OBJ CV0($o) string("prop") +0003 OP_DATA CV1($b) +0004 RETURN null diff --git a/PHP/23_object_argument/1_instance_23_object_argument/1_instance_23_object_argument.json b/PHP/23_object_argument/1_instance_23_object_argument/1_instance_23_object_argument.json old mode 100644 new mode 100755 index 9168bbd..fe918f6 --- a/PHP/23_object_argument/1_instance_23_object_argument/1_instance_23_object_argument.json +++ b/PHP/23_object_argument/1_instance_23_object_argument/1_instance_23_object_argument.json @@ -1,37 +1,38 @@ -{ - "code": { - "path": "./1_instance_23_object_argument.php", - "injection_skeleton_broken": false - }, - "discovery": { - "rule": "./1_instance_23_object_argument.sc", - "method": "joern", - "rule_accuracy": null, - "notes": null - }, - "compile": { - "binary": "./1_instance_23_object_argument.bash", - "instruction": null, - "dependencies": null - }, - "expectation": { - "type": "xss", - "sink_file": "./1_instance_23_object_argument.php", - "sink_line": 13, - "source_file": "./1_instance_23_object_argument.php", - "source_line": 10, - "expectation": true - }, - "properties": { - "category": "S0", - "feature_vs_internal_api": "FEATURE", - "input_sanitizer": false, - "source_and_sink": false, - "negative_test_case": false - }, - "remediation": { - "notes": "", - "transformation": null, - "modeling_rule": null - } +{ + "description": "This instance passes an object to a function, the function modifies the object itself.", + "code": { + "path": "./1_instance_23_object_argument.php", + "injection_skeleton_broken": false + }, + "discovery": { + "rule": "./1_instance_23_object_argument.sc", + "method": "joern", + "rule_accuracy": "FP", + "notes": "The rule first collects all `ASSIGN` calls, that are reachable by a `NEW` call in `G1`.\nIn `R2` all calls to `SEND` using a variable are gathered.\nIn the end all of the `SEND` calls are filtered and only those taken, where any element of `G1` is included." + }, + "compile": { + "binary": "./1_instance_23_object_argument.bash", + "instruction": null, + "dependencies": null + }, + "expectation": { + "type": "xss", + "sink_file": "./1_instance_23_object_argument.php", + "sink_line": 13, + "source_file": "./1_instance_23_object_argument.php", + "source_line": 10, + "expectation": true + }, + "properties": { + "category": "S0", + "feature_vs_internal_api": "FEATURE", + "input_sanitizer": false, + "source_and_sink": false, + "negative_test_case": false + }, + "remediation": { + "notes": "", + "transformation": null, + "modeling_rule": null + } } \ No newline at end of file diff --git a/PHP/23_object_argument/1_instance_23_object_argument/1_instance_23_object_argument.php b/PHP/23_object_argument/1_instance_23_object_argument/1_instance_23_object_argument.php old mode 100644 new mode 100755 index 03103ed..f93987c --- a/PHP/23_object_argument/1_instance_23_object_argument/1_instance_23_object_argument.php +++ b/PHP/23_object_argument/1_instance_23_object_argument/1_instance_23_object_argument.php @@ -1,13 +1,13 @@ -prop = 'abc'; - -// There is no & for $o -function f ( $o, $b ) { - $o->prop = $b; -} - -$b = $_GET["p1"]; -f($x,$b); -// XSS vulnerability, it will print $b -echo $x->prop; \ No newline at end of file +prop = 'abc'; + +// There is no & for $o +function f ( $o, $b ) { + $o->prop = $b; +} + +$b = $_GET["p1"]; // source +f($x,$b); +// XSS vulnerability, it will print $b +echo $x->prop; // sink \ No newline at end of file diff --git a/PHP/23_object_argument/1_instance_23_object_argument/1_instance_23_object_argument.sc b/PHP/23_object_argument/1_instance_23_object_argument/1_instance_23_object_argument.sc old mode 100644 new mode 100755 index ac32125..4a331b1 --- a/PHP/23_object_argument/1_instance_23_object_argument/1_instance_23_object_argument.sc +++ b/PHP/23_object_argument/1_instance_23_object_argument/1_instance_23_object_argument.sc @@ -1,8 +1,8 @@ -@main def main(name : String): Unit = { - importCpg(name) - val G1 = cpg.call("ASSIGN").reachableByFlows(cpg.call.code(".*NEW.*")).map(_.elements.last).collect{ case c : nodes.Call => c}.argument.order(0).isIdentifier.name.l.distinct - val R2 = cpg.call("SEND.*").code(".*CV.*").l - val x23 = (name, "23_object_argument_iall", R2.filter{ call => G1.exists{ h => call.argument.order(0).code.l.contains("CV($" + h + ")")} }.location.toJson); - println(x23) - delete; +@main def main(name : String): Unit = { + importCpg(name) + val G1 = cpg.call("ASSIGN").reachableByFlows(cpg.call.code(".*NEW.*")).map(_.elements.last).collect{ case c : nodes.Call => c}.argument.order(0).isIdentifier.name.l.distinct + val R2 = cpg.call("SEND.*").code(".*CV.*").l + val x23 = (name, "23_object_argument_iall", R2.filter{ call => G1.exists{ h => call.argument.order(0).code.l.contains("CV($" + h + ")")} }.location.toJson); + println(x23) + delete; } \ No newline at end of file diff --git a/PHP/23_object_argument/23_object_argument.json b/PHP/23_object_argument/23_object_argument.json old mode 100644 new mode 100755 index 4854913..63511bc --- a/PHP/23_object_argument/23_object_argument.json +++ b/PHP/23_object_argument/23_object_argument.json @@ -1,14 +1,14 @@ -{ - "name": "Object Argument", - "description": "", - "family": "code_pattern_php", - "tags": [ - "sast", - "php", - "php_v7.4.9" - ], - "instances": [ - "./1_instance_23_object_argument/1_instance_23_object_argument.json" - ], - "version": "v0.draft" +{ + "name": "Object Argument", + "description": "./docs/description.md", + "family": "code_pattern_php", + "tags": [ + "sast", + "php", + "php_v7.4.9" + ], + "instances": [ + "./1_instance_23_object_argument/1_instance_23_object_argument.json" + ], + "version": "v1.0" } \ No newline at end of file diff --git a/PHP/23_object_argument/README.md b/PHP/23_object_argument/README.md old mode 100644 new mode 100755 index f0c6fff..ad7002d --- a/PHP/23_object_argument/README.md +++ b/PHP/23_object_argument/README.md @@ -1,101 +1,130 @@ -# Pattern: Object Argument - -## Category - -Objects - -## Definition - -## Instances - -### Instance 1 - -- CATEGORY: S0 -- FEATURE vs INTERNAL API: FEATURE -- INPUT SANITIZERS: NO -- SOURCES AND SINKS: NO -- NEGATIVE TEST CASES: NO -- CODE: - -```php -prop = 'abc'; - -// There is no & for $o -function f ( $o, $b ) { - $o->prop = $b; -} - -$b = $_GET["p1"]; -f($x,$b); -// XSS vulnerability, it will print $b -echo $x->prop; -``` - -- MEASUREMENT: - -| Tool | RIPS | phpSAFE | WAP | Progpilot | Comm_1 | Comm_2 | Correct | -| ------------- | ---- | ------- | ---- | --------- | ------- | --------- | ------- | -| Vulnerability | NO | NO | NO | NO | YES | NO | YES | -Measurements Date: 8 June 2021 - -- OPCODE: - -```bash -$_main: ; (lines=20, args=0, vars=2, tmps=9) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/25_object_argument/25_object_argument.php:1-13 -L0 (2): EXT_STMT -L1 (2): V2 = NEW 0 string("stdClass") -L2 (2): DO_FCALL -L3 (2): ASSIGN CV0($x) V2 -L4 (3): EXT_STMT -L5 (3): ASSIGN_OBJ CV0($x) string("prop") -L6 (3): OP_DATA string("abc") -L7 (10): EXT_STMT -L8 (10): T6 = FETCH_R (global) string("_GET") -L9 (10): T7 = FETCH_DIM_R T6 string("p1") -L10 (10): ASSIGN CV1($b) T7 -L11 (11): EXT_STMT -L12 (11): INIT_FCALL 2 128 string("f") -L13 (11): SEND_VAR CV0($x) 1 -L14 (11): SEND_VAR CV1($b) 2 -L15 (11): DO_FCALL -L16 (13): EXT_STMT -L17 (13): T10 = FETCH_OBJ_R CV0($x) string("prop") -L18 (13): ECHO T10 -L19 (13): RETURN int(1) -LIVE RANGES: - 2: L2 - L3 (new) - -f: ; (lines=8, args=2, vars=2, tmps=1) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/25_object_argument/25_object_argument.php:6-8 -L0 (6): EXT_NOP -L1 (6): CV0($o) = RECV 1 -L2 (6): CV1($b) = RECV 2 -L3 (7): EXT_STMT -L4 (7): ASSIGN_OBJ CV0($o) string("prop") -L5 (7): OP_DATA CV1($b) -L6 (8): EXT_STMT -L7 (8): RETURN null -``` - -- DISCOVERY: - -```bash -val G1 = cpg.call("ASSIGN").reachableByFlows(cpg.call.code(".*NEW.*")).map(_.elements.last).collect{ case c : nodes.Call => c}.argument.order(0).isIdentifier.name.l.distinct -val R2 = cpg.call("SEND.*").code(".*CV.*").l -R2.filter{ call => G1.exists{ h => call.argument.order(0).code.l.contains("CV($" + h + ")")} }.size; -``` - -- PRECONDITIONS: - 1. - -- TRANSFORMATION: - -``` - -``` - +[//]: # (This file is automatically generated. If you wish to make any changes, please use the JSON files and regenerate this file using the tpframework.) + +# Object Argument + +Tags: sast, php, php_v7.4.9 + +Version: v1.0 + +## Description + +When an object is passed to a function as an argument, PHP wil pass it as reference, so every change made to the object inside the function will also affect the object itself. + +## Overview + +| Instances | has discovery rule | discovery method | rule successfull | +|---------------------------|----------------------|--------------------|--------------------| +| [1 Instance](#1-instance) | yes | joern | yes | + +## 1 Instance + +This instance passes an object to a function, the function modifies the object itself. + +### Code + +```PHP +prop = 'abc'; + +// There is no & for $o +function f ( $o, $b ) { + $o->prop = $b; +} + +$b = $_GET["p1"]; // source +f($x,$b); +// XSS vulnerability, it will print $b +echo $x->prop; // sink +``` + +### Instance Properties + +| category | feature_vs_internal_api | input_sanitizer | negative_test_case | source_and_sink | +|------------|---------------------------|-------------------|----------------------|-------------------| +| S0 | FEATURE | no | no | no | + +
+ +More + +
+ + +### Compile + + +```bash +$_main: + ; (lines=15, args=0, vars=2, tmps=9) + ; (before optimizer) + ; /.../PHP/23_object_argument/1_instance_23_object_argument/1_instance_23_object_argument.php:1-13 + ; return [] RANGE[0..0] +0000 V2 = NEW 0 string("stdClass") +0001 DO_FCALL +0002 ASSIGN CV0($x) V2 +0003 ASSIGN_OBJ CV0($x) string("prop") +0004 OP_DATA string("abc") +0005 T6 = FETCH_R (global) string("_GET") +0006 T7 = FETCH_DIM_R T6 string("p1") +0007 ASSIGN CV1($b) T7 +0008 INIT_FCALL 2 128 string("f") +0009 SEND_VAR CV0($x) 1 +0010 SEND_VAR CV1($b) 2 +0011 DO_UCALL +0012 T10 = FETCH_OBJ_R CV0($x) string("prop") +0013 ECHO T10 +0014 RETURN int(1) +LIVE RANGES: + 2: 0001 - 0002 (new) + +f: + ; (lines=5, args=2, vars=2, tmps=1) + ; (before optimizer) + ; /.../PHP/23_object_argument/1_instance_23_object_argument/1_instance_23_object_argument.php:6-8 + ; return [] RANGE[0..0] +0000 CV0($o) = RECV 1 +0001 CV1($b) = RECV 2 +0002 ASSIGN_OBJ CV0($o) string("prop") +0003 OP_DATA CV1($b) +0004 RETURN null +``` + +
+ +
+ + +### Discovery + + +The rule first collects all `ASSIGN` calls, that are reachable by a `NEW` call in `G1`. +In `R2` all calls to `SEND` using a variable are gathered. +In the end all of the `SEND` calls are filtered and only those taken, where any element of `G1` is included. + +```scala +val G1 = cpg.call("ASSIGN").reachableByFlows(cpg.call.code(".*NEW.*")).map(_.elements.last).collect{ case c : nodes.Call => c}.argument.order(0).isIdentifier.name.l.distinct +val R2 = cpg.call("SEND.*").code(".*CV.*").l +val x23 = (name, "23_object_argument_iall", R2.filter{ call => G1.exists{ h => call.argument.order(0).code.l.contains("CV($" + h + ")")} }.location.toJson); +``` + +| discovery method | expected accuracy | +|--------------------|---------------------| +| joern | FP | + +
+ +
+ + +### Measurement + + +| Tool | Comm_1 | Comm_2 | phpSAFE | Progpilot | RIPS | WAP | Ground Truth | +|-------------|----------|----------|-----------|-------------|--------|-------|----------------| +| 08 Jun 2021 | yes | no | no | no | no | no | yes | +| 17 May 2023 | yes | no | | | | | yes | + +
+ +
diff --git a/PHP/23_object_argument/docs/description.md b/PHP/23_object_argument/docs/description.md new file mode 100755 index 0000000..7d87a6a --- /dev/null +++ b/PHP/23_object_argument/docs/description.md @@ -0,0 +1 @@ +When an object is passed to a function as an argument, PHP wil pass it as reference, so every change made to the object inside the function will also affect the object itself. \ No newline at end of file diff --git a/PHP/24_new_self/1_instance_24_new_self/1_instance_24_new_self.bash b/PHP/24_new_self/1_instance_24_new_self/1_instance_24_new_self.bash old mode 100644 new mode 100755 index 57738d9..6435f7c --- a/PHP/24_new_self/1_instance_24_new_self/1_instance_24_new_self.bash +++ b/PHP/24_new_self/1_instance_24_new_self/1_instance_24_new_self.bash @@ -1,53 +1,51 @@ - -$_main: ; (lines=9, args=0, vars=2, tmps=5) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/23_new_self/23_new_self.php:1-23 -L0 (21): EXT_STMT -L1 (21): V2 = NEW 0 string("myclass") -L2 (21): DO_FCALL -L3 (21): ASSIGN CV0($obj) V2 -L4 (22): EXT_STMT -L5 (22): INIT_METHOD_CALL 0 CV0($obj) string("F") -L6 (22): V5 = DO_FCALL -L7 (22): ASSIGN CV1($obj2) V5 -L8 (23): RETURN int(1) -LIVE RANGES: - 2: L2 - L3 (new) - -myclass::__construct: ; (lines=8, args=0, vars=0, tmps=3) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/23_new_self/23_new_self.php:5-8 -L0 (5): EXT_NOP -L1 (6): EXT_STMT -L2 (6): T1 = FETCH_R (global) string("_GET") -L3 (6): T2 = FETCH_DIM_R T1 string("p1") -L4 (6): ASSIGN_OBJ THIS string("b") -L5 (6): OP_DATA T2 -L6 (8): EXT_STMT -L7 (8): RETURN null - -myclass::F: ; (lines=10, args=0, vars=1, tmps=4) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/23_new_self/23_new_self.php:10-13 -L0 (10): EXT_NOP -L1 (11): EXT_STMT -L2 (11): V1 = NEW 0 (self) (exception) -L3 (11): DO_FCALL -L4 (11): ASSIGN CV0($obj2) V1 -L5 (12): EXT_STMT -L6 (12): INIT_METHOD_CALL 0 CV0($obj2) string("T") -L7 (12): DO_FCALL -L8 (13): EXT_STMT -L9 (13): RETURN null -LIVE RANGES: - 1: L3 - L4 (new) - -myclass::T: ; (lines=6, args=0, vars=0, tmps=1) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/23_new_self/23_new_self.php:15-17 -L0 (15): EXT_NOP -L1 (16): EXT_STMT -L2 (16): T0 = FETCH_OBJ_R THIS string("b") -L3 (16): ECHO T0 -L4 (17): EXT_STMT -L5 (17): RETURN null + +$_main: + ; (lines=8, args=0, vars=2, tmps=5) + ; (before optimizer) + ; /.../PHP/24_new_self/1_instance_24_new_self/1_instance_24_new_self.php:1-22 + ; return [] RANGE[0..0] +0000 V2 = NEW 0 string("myclass") +0001 DO_FCALL +0002 ASSIGN CV0($obj) V2 +0003 INIT_METHOD_CALL 0 CV0($obj) string("F") +0004 V5 = DO_FCALL +0005 ASSIGN CV1($a) V5 +0006 ECHO CV1($a) +0007 RETURN int(1) +LIVE RANGES: + 2: 0001 - 0002 (new) + +myclass::__construct: + ; (lines=5, args=0, vars=0, tmps=3) + ; (before optimizer) + ; /.../PHP/24_new_self/1_instance_24_new_self/1_instance_24_new_self.php:4-6 + ; return [] RANGE[0..0] +0000 T1 = FETCH_R (global) string("_GET") +0001 T2 = FETCH_DIM_R T1 string("p1") +0002 ASSIGN_OBJ THIS string("b") +0003 OP_DATA T2 +0004 RETURN null + +myclass::F: + ; (lines=7, args=0, vars=1, tmps=4) + ; (before optimizer) + ; /.../PHP/24_new_self/1_instance_24_new_self/1_instance_24_new_self.php:8-11 + ; return [] RANGE[0..0] +0000 V1 = NEW 0 (self) (exception) +0001 DO_FCALL +0002 ASSIGN CV0($obj2) V1 +0003 INIT_METHOD_CALL 0 CV0($obj2) string("T") +0004 V4 = DO_FCALL +0005 RETURN V4 +0006 RETURN null +LIVE RANGES: + 1: 0001 - 0002 (new) + +myclass::T: + ; (lines=3, args=0, vars=0, tmps=1) + ; (before optimizer) + ; /.../PHP/24_new_self/1_instance_24_new_self/1_instance_24_new_self.php:13-15 + ; return [] RANGE[0..0] +0000 T0 = FETCH_OBJ_R THIS string("b") +0001 RETURN T0 +0002 RETURN null diff --git a/PHP/24_new_self/1_instance_24_new_self/1_instance_24_new_self.json b/PHP/24_new_self/1_instance_24_new_self/1_instance_24_new_self.json old mode 100644 new mode 100755 index 59e2ac1..355bcc2 --- a/PHP/24_new_self/1_instance_24_new_self/1_instance_24_new_self.json +++ b/PHP/24_new_self/1_instance_24_new_self/1_instance_24_new_self.json @@ -1,37 +1,38 @@ -{ - "code": { - "path": "./1_instance_24_new_self.php", - "injection_skeleton_broken": false - }, - "discovery": { - "rule": "./1_instance_24_new_self.sc", - "method": "joern", - "rule_accuracy": null, - "notes": null - }, - "compile": { - "binary": "./1_instance_24_new_self.bash", - "instruction": null, - "dependencies": null - }, - "expectation": { - "type": "xss", - "sink_file": "./1_instance_24_new_self.php", - "sink_line": 16, - "source_file": "./1_instance_24_new_self.php", - "source_line": 6, - "expectation": true - }, - "properties": { - "category": "S0", - "feature_vs_internal_api": "FEATURE", - "input_sanitizer": false, - "source_and_sink": false, - "negative_test_case": false - }, - "remediation": { - "notes": "", - "transformation": null, - "modeling_rule": null - } +{ + "description": "The instance demonstrates the use of `new self`, with a source in the `__construct()` call.", + "code": { + "path": "./1_instance_24_new_self.php", + "injection_skeleton_broken": true + }, + "discovery": { + "rule": "./1_instance_24_new_self.sc", + "method": "joern", + "rule_accuracy": "Perfect", + "notes": "The rule searches for calles to `NEW`, which have `self` as an argument." + }, + "compile": { + "binary": "./1_instance_24_new_self.bash", + "instruction": null, + "dependencies": null + }, + "expectation": { + "type": "xss", + "sink_file": "./1_instance_24_new_self.php", + "sink_line": 21, + "source_file": "./1_instance_24_new_self.php", + "source_line": 5, + "expectation": true + }, + "properties": { + "category": "S0", + "feature_vs_internal_api": "FEATURE", + "input_sanitizer": false, + "source_and_sink": false, + "negative_test_case": false + }, + "remediation": { + "notes": "", + "transformation": null, + "modeling_rule": null + } } \ No newline at end of file diff --git a/PHP/24_new_self/1_instance_24_new_self/1_instance_24_new_self.php b/PHP/24_new_self/1_instance_24_new_self/1_instance_24_new_self.php old mode 100644 new mode 100755 index 4da9768..739a584 --- a/PHP/24_new_self/1_instance_24_new_self/1_instance_24_new_self.php +++ b/PHP/24_new_self/1_instance_24_new_self/1_instance_24_new_self.php @@ -1,22 +1,21 @@ -b = $_GET['p1']; - //$this->b = "abc"; -} - -function F(){ - $obj2 = new self; - $obj2->T(); -} - -function T(){ - echo $this->b; -} -} - - -$obj = new myclass(); -$obj2 = $obj->F(); +b = $_GET['p1']; // source + } + + function F() { + $obj2 = new self; + return $obj2->T(); + } + + function T() { + return $this->b; + } +} + + +$obj = new myclass(); +$a = $obj->F(); +echo $a; // sink diff --git a/PHP/24_new_self/1_instance_24_new_self/1_instance_24_new_self.sc b/PHP/24_new_self/1_instance_24_new_self/1_instance_24_new_self.sc old mode 100644 new mode 100755 index 1ab0c79..47fa9a1 --- a/PHP/24_new_self/1_instance_24_new_self/1_instance_24_new_self.sc +++ b/PHP/24_new_self/1_instance_24_new_self/1_instance_24_new_self.sc @@ -1,6 +1,6 @@ -@main def main(name : String): Unit = { - importCpg(name) - val x24 = (name, "24_new_self_iall", cpg.call(".*NEW.*").argument.order(1).code("self").astParent.location.toJson); - println(x24) - delete; +@main def main(name : String): Unit = { + importCpg(name) + val x24 = (name, "24_new_self_iall", cpg.call(".*NEW.*").argument.order(1).code("self").astParent.location.toJson); + println(x24) + delete; } \ No newline at end of file diff --git a/PHP/24_new_self/24_new_self.json b/PHP/24_new_self/24_new_self.json old mode 100644 new mode 100755 index c18f87c..93863c1 --- a/PHP/24_new_self/24_new_self.json +++ b/PHP/24_new_self/24_new_self.json @@ -1,14 +1,14 @@ -{ - "name": "New Self", - "description": "", - "family": "code_pattern_php", - "tags": [ - "sast", - "php", - "php_v7.4.9" - ], - "instances": [ - "./1_instance_24_new_self/1_instance_24_new_self.json" - ], - "version": "v0.draft" +{ + "name": "New Self", + "description": "./docs/description.md", + "family": "code_pattern_php", + "tags": [ + "sast", + "php", + "php_v7.4.9" + ], + "instances": [ + "./1_instance_24_new_self/1_instance_24_new_self.json" + ], + "version": "v1.0" } \ No newline at end of file diff --git a/PHP/24_new_self/README.md b/PHP/24_new_self/README.md old mode 100644 new mode 100755 index 2e7dcf0..813d420 --- a/PHP/24_new_self/README.md +++ b/PHP/24_new_self/README.md @@ -1,144 +1,150 @@ -# Pattern: New Self - -## Category - -Objects - -## Definition - -## Instances - -### Instance 1 - -- CATEGORY: S0 -- FEATURE vs INTERNAL API: FEATURE -- INPUT SANITIZERS: NO -- SOURCES AND SINKS: NO -- NEGATIVE TEST CASES: NO -- CODE: - -```php -b = $_GET['p1']; - //$this->b = "abc"; - } - - function F(){ - $obj2 = new self; - $obj2->T(); - } - - function T(){ - echo $this->b; - } -} - - -$obj = new myclass(); -$obj2 = $obj->F(); -``` - -- MEASUREMENT: - -| Tool | RIPS | phpSAFE | WAP | Progpilot | Comm_1 | Comm_2 | Correct | -| ------------- | ---- | ------- | ---- | --------- | ------- | --------- | ------- | -| Vulnerability | NO | NO |NO | NO | YES | NO | YES | -Measurements Date: 8 June 2021 - -- OPCODE: - -```bash -$_main: ; (lines=9, args=0, vars=2, tmps=5) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/23_new_self/23_new_self.php:1-23 -L0 (21): EXT_STMT -L1 (21): V2 = NEW 0 string("myclass") -L2 (21): DO_FCALL -L3 (21): ASSIGN CV0($obj) V2 -L4 (22): EXT_STMT -L5 (22): INIT_METHOD_CALL 0 CV0($obj) string("F") -L6 (22): V5 = DO_FCALL -L7 (22): ASSIGN CV1($obj2) V5 -L8 (23): RETURN int(1) -LIVE RANGES: - 2: L2 - L3 (new) - -myclass::__construct: ; (lines=8, args=0, vars=0, tmps=3) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/23_new_self/23_new_self.php:5-8 -L0 (5): EXT_NOP -L1 (6): EXT_STMT -L2 (6): T1 = FETCH_R (global) string("_GET") -L3 (6): T2 = FETCH_DIM_R T1 string("p1") -L4 (6): ASSIGN_OBJ THIS string("b") -L5 (6): OP_DATA T2 -L6 (8): EXT_STMT -L7 (8): RETURN null - -myclass::F: ; (lines=10, args=0, vars=1, tmps=4) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/23_new_self/23_new_self.php:10-13 -L0 (10): EXT_NOP -L1 (11): EXT_STMT -L2 (11): V1 = NEW 0 (self) (exception) -L3 (11): DO_FCALL -L4 (11): ASSIGN CV0($obj2) V1 -L5 (12): EXT_STMT -L6 (12): INIT_METHOD_CALL 0 CV0($obj2) string("T") -L7 (12): DO_FCALL -L8 (13): EXT_STMT -L9 (13): RETURN null -LIVE RANGES: - 1: L3 - L4 (new) - -myclass::T: ; (lines=6, args=0, vars=0, tmps=1) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/23_new_self/23_new_self.php:15-17 -L0 (15): EXT_NOP -L1 (16): EXT_STMT -L2 (16): T0 = FETCH_OBJ_R THIS string("b") -L3 (16): ECHO T0 -L4 (17): EXT_STMT -L5 (17): RETURN null -``` - -- DISCOVERY: - -```bash -cpg.call(".*NEW.*").argument.order(1).code("self").location.l -``` - -- PRECONDITIONS: - 1. - -- TRANSFORMATION: - -```php -b = $_GET['p1']; - //$this->b = "abc"; - } - - function F(){ - $obj2 = new myclass(); - $obj2->T(); - } - - function T(){ - echo $this->b; - } -} - - -$obj = new myclass(); -$obj2 = $obj->F(); -``` - +[//]: # (This file is automatically generated. If you wish to make any changes, please use the JSON files and regenerate this file using the tpframework.) + +# New Self + +Tags: sast, php, php_v7.4.9 + +Version: v1.0 + +## Description + +In PHP `new self` is used to create a new instance of the same class in which it appears. + +## Overview + +| Instances | has discovery rule | discovery method | rule successfull | +|---------------------------|----------------------|--------------------|--------------------| +| [1 Instance](#1-instance) | yes | joern | yes | + +## 1 Instance + +The instance demonstrates the use of `new self`, with a source in the `__construct()` call. + +### Code + +```PHP +b = $_GET['p1']; // source + } + + function F() { + $obj2 = new self; + return $obj2->T(); + } + + function T() { + return $this->b; + } +} + +$obj = new myclass(); +$a = $obj->F(); +echo $a; // sink +``` + +### Instance Properties + +| category | feature_vs_internal_api | input_sanitizer | negative_test_case | source_and_sink | +|------------|---------------------------|-------------------|----------------------|-------------------| +| S0 | FEATURE | no | no | no | + +
+ +More + +
+ + +### Compile + + +```bash +$_main: + ; (lines=8, args=0, vars=2, tmps=5) + ; (before optimizer) + ; /.../PHP/24_new_self/1_instance_24_new_self/1_instance_24_new_self.php:1-22 + ; return [] RANGE[0..0] +0000 V2 = NEW 0 string("myclass") +0001 DO_FCALL +0002 ASSIGN CV0($obj) V2 +0003 INIT_METHOD_CALL 0 CV0($obj) string("F") +0004 V5 = DO_FCALL +0005 ASSIGN CV1($a) V5 +0006 ECHO CV1($a) +0007 RETURN int(1) +LIVE RANGES: + 2: 0001 - 0002 (new) + +myclass::__construct: + ; (lines=5, args=0, vars=0, tmps=3) + ; (before optimizer) + ; /.../PHP/24_new_self/1_instance_24_new_self/1_instance_24_new_self.php:4-6 + ; return [] RANGE[0..0] +0000 T1 = FETCH_R (global) string("_GET") +0001 T2 = FETCH_DIM_R T1 string("p1") +0002 ASSIGN_OBJ THIS string("b") +0003 OP_DATA T2 +0004 RETURN null + +myclass::F: + ; (lines=7, args=0, vars=1, tmps=4) + ; (before optimizer) + ; /.../PHP/24_new_self/1_instance_24_new_self/1_instance_24_new_self.php:8-11 + ; return [] RANGE[0..0] +0000 V1 = NEW 0 (self) (exception) +0001 DO_FCALL +0002 ASSIGN CV0($obj2) V1 +0003 INIT_METHOD_CALL 0 CV0($obj2) string("T") +0004 V4 = DO_FCALL +0005 RETURN V4 +0006 RETURN null +LIVE RANGES: + 1: 0001 - 0002 (new) + +myclass::T: + ; (lines=3, args=0, vars=0, tmps=1) + ; (before optimizer) + ; /.../PHP/24_new_self/1_instance_24_new_self/1_instance_24_new_self.php:13-15 + ; return [] RANGE[0..0] +0000 T0 = FETCH_OBJ_R THIS string("b") +0001 RETURN T0 +0002 RETURN null +``` + +
+ +
+ + +### Discovery + + +The rule searches for calles to `NEW`, which have `self` as an argument. + +```scala +val x24 = (name, "24_new_self_iall", cpg.call(".*NEW.*").argument.order(1).code("self").astParent.location.toJson); +``` + +| discovery method | expected accuracy | +|--------------------|---------------------| +| joern | Perfect | + +
+ +
+ + +### Measurement + + +| Tool | Comm_1 | Comm_2 | phpSAFE | Progpilot | RIPS | WAP | Ground Truth | +|-------------|----------|----------|-----------|-------------|--------|-------|----------------| +| 08 Jun 2021 | yes | no | no | no | no | no | yes | +| 17 May 2023 | yes | no | | | | | yes | + +
+ +
diff --git a/PHP/24_new_self/docs/description.md b/PHP/24_new_self/docs/description.md new file mode 100755 index 0000000..3b89cf8 --- /dev/null +++ b/PHP/24_new_self/docs/description.md @@ -0,0 +1 @@ +In PHP `new self` is used to create a new instance of the same class in which it appears. \ No newline at end of file diff --git a/PHP/25_clone/1_instance_25_clone/1_instance_25_clone.bash b/PHP/25_clone/1_instance_25_clone/1_instance_25_clone.bash old mode 100644 new mode 100755 index 98b5c0f..a3b128f --- a/PHP/25_clone/1_instance_25_clone/1_instance_25_clone.bash +++ b/PHP/25_clone/1_instance_25_clone/1_instance_25_clone.bash @@ -1,44 +1,41 @@ - -$_main: ; (lines=17, args=0, vars=3, tmps=9) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/26_clone/26_clone.php:1-19 -L0 (4): NOP -L1 (14): EXT_STMT -L2 (14): T3 = FETCH_R (global) string("_GET") -L3 (14): T4 = FETCH_DIM_R T3 string("p1") -L4 (14): ASSIGN CV0($b) T4 -L5 (15): EXT_STMT -L6 (15): V6 = NEW 1 string("Foo") -L7 (15): SEND_VAR_EX CV0($b) 1 -L8 (15): DO_FCALL -L9 (15): ASSIGN CV1($ob1) V6 -L10 (16): EXT_STMT -L11 (16): T9 = CLONE CV1($ob1) -L12 (16): ASSIGN CV2($ob2) T9 -L13 (17): EXT_STMT -L14 (17): INIT_METHOD_CALL 0 CV2($ob2) string("baz") -L15 (17): DO_FCALL -L16 (19): RETURN int(1) -LIVE RANGES: - 6: L7 - L9 (new) - -Foo::__construct: ; (lines=7, args=1, vars=1, tmps=1) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/26_clone/26_clone.php:5-7 -L0 (5): EXT_NOP -L1 (5): CV0($b) = RECV 1 -L2 (6): EXT_STMT -L3 (6): ASSIGN_OBJ THIS string("b") -L4 (6): OP_DATA CV0($b) -L5 (7): EXT_STMT -L6 (7): RETURN null - -Foo::baz: ; (lines=6, args=0, vars=0, tmps=1) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/26_clone/26_clone.php:8-12 -L0 (8): EXT_NOP -L1 (11): EXT_STMT -L2 (11): T0 = FETCH_OBJ_R THIS string("b") -L3 (11): ECHO T0 -L4 (12): EXT_STMT -L5 (12): RETURN null + +$_main: + ; (lines=14, args=0, vars=4, tmps=10) + ; (before optimizer) + ; /.../PHP/25_clone/1_instance_25_clone/1_instance_25_clone.php:1-16 + ; return [] RANGE[0..0] +0000 T4 = FETCH_R (global) string("_GET") +0001 T5 = FETCH_DIM_R T4 string("p1") +0002 ASSIGN CV0($b) T5 +0003 V7 = NEW 1 string("Foo") +0004 SEND_VAR_EX CV0($b) 1 +0005 DO_FCALL +0006 ASSIGN CV1($ob1) V7 +0007 T10 = CLONE CV1($ob1) +0008 ASSIGN CV2($ob2) T10 +0009 INIT_METHOD_CALL 0 CV2($ob2) string("baz") +0010 V12 = DO_FCALL +0011 ASSIGN CV3($a) V12 +0012 ECHO CV3($a) +0013 RETURN int(1) +LIVE RANGES: + 7: 0004 - 0006 (new) + +Foo::__construct: + ; (lines=4, args=1, vars=1, tmps=1) + ; (before optimizer) + ; /.../PHP/25_clone/1_instance_25_clone/1_instance_25_clone.php:4-6 + ; return [] RANGE[0..0] +0000 CV0($b) = RECV 1 +0001 ASSIGN_OBJ THIS string("b") +0002 OP_DATA CV0($b) +0003 RETURN null + +Foo::baz: + ; (lines=3, args=0, vars=0, tmps=1) + ; (before optimizer) + ; /.../PHP/25_clone/1_instance_25_clone/1_instance_25_clone.php:7-10 + ; return [] RANGE[0..0] +0000 T0 = FETCH_OBJ_R THIS string("b") +0001 RETURN T0 +0002 RETURN null diff --git a/PHP/25_clone/1_instance_25_clone/1_instance_25_clone.json b/PHP/25_clone/1_instance_25_clone/1_instance_25_clone.json old mode 100644 new mode 100755 index bb05015..9c47f03 --- a/PHP/25_clone/1_instance_25_clone/1_instance_25_clone.json +++ b/PHP/25_clone/1_instance_25_clone/1_instance_25_clone.json @@ -1,37 +1,38 @@ -{ - "code": { - "path": "./1_instance_25_clone.php", - "injection_skeleton_broken": false - }, - "discovery": { - "rule": "./1_instance_25_clone.sc", - "method": "joern", - "rule_accuracy": null, - "notes": null - }, - "compile": { - "binary": "./1_instance_25_clone.bash", - "instruction": null, - "dependencies": null - }, - "expectation": { - "type": "xss", - "sink_file": "./1_instance_25_clone.php", - "sink_line": 11, - "source_file": "./1_instance_25_clone.php", - "source_line": 14, - "expectation": true - }, - "properties": { - "category": "S0", - "feature_vs_internal_api": "FEATURE", - "input_sanitizer": false, - "source_and_sink": false, - "negative_test_case": false - }, - "remediation": { - "notes": "", - "transformation": null, - "modeling_rule": null - } +{ + "description": "This instance demonstrates, that when you clone an object, both the cloned object and the original object have the same value.", + "code": { + "path": "./1_instance_25_clone.php", + "injection_skeleton_broken": false + }, + "discovery": { + "rule": "./1_instance_25_clone.sc", + "method": "joern", + "rule_accuracy": "Perfect", + "notes": "The rule searches for the opcode `CLONE`." + }, + "compile": { + "binary": "./1_instance_25_clone.bash", + "instruction": null, + "dependencies": null + }, + "expectation": { + "type": "xss", + "sink_file": "./1_instance_25_clone.php", + "sink_line": 16, + "source_file": "./1_instance_25_clone.php", + "source_line": 12, + "expectation": true + }, + "properties": { + "category": "S0", + "feature_vs_internal_api": "FEATURE", + "input_sanitizer": false, + "source_and_sink": false, + "negative_test_case": false + }, + "remediation": { + "notes": "", + "transformation": null, + "modeling_rule": null + } } \ No newline at end of file diff --git a/PHP/25_clone/1_instance_25_clone/1_instance_25_clone.php b/PHP/25_clone/1_instance_25_clone/1_instance_25_clone.php old mode 100644 new mode 100755 index 6697cbb..de744da --- a/PHP/25_clone/1_instance_25_clone/1_instance_25_clone.php +++ b/PHP/25_clone/1_instance_25_clone/1_instance_25_clone.php @@ -1,19 +1,16 @@ -b = $b; - } - public function baz() - { - // $b has the input, XSS Vulnerability - echo $this->b; - } -} -$b = $_GET["p1"]; -$ob1 = new Foo($b); -$ob2 = clone $ob1; -$ob2->baz(); -//$ob->baz(); -//array(new Foo($b), "baz")(); \ No newline at end of file +b = $b; + } + public function baz() { + // $b has the input, XSS Vulnerability + return $this->b; + } +} +$b = $_GET["p1"]; // source +$ob1 = new Foo($b); +$ob2 = clone $ob1; // tarpit +$a = $ob2->baz(); +echo $a; // sink \ No newline at end of file diff --git a/PHP/25_clone/1_instance_25_clone/1_instance_25_clone.sc b/PHP/25_clone/1_instance_25_clone/1_instance_25_clone.sc old mode 100644 new mode 100755 index 0621174..e9d3c93 --- a/PHP/25_clone/1_instance_25_clone/1_instance_25_clone.sc +++ b/PHP/25_clone/1_instance_25_clone/1_instance_25_clone.sc @@ -1,6 +1,6 @@ -@main def main(name : String): Unit = { - importCpg(name) - val x25 = (name, "25_clone_iall", cpg.call(".*CLONE.*").location.toJson); - println(x25) - delete; +@main def main(name : String): Unit = { + importCpg(name) + val x25 = (name, "25_clone_iall", cpg.call(".*CLONE.*").location.toJson); + println(x25) + delete; } \ No newline at end of file diff --git a/PHP/25_clone/25_clone.json b/PHP/25_clone/25_clone.json old mode 100644 new mode 100755 index 427bb71..7f2ce18 --- a/PHP/25_clone/25_clone.json +++ b/PHP/25_clone/25_clone.json @@ -1,14 +1,14 @@ -{ - "name": "Clone", - "description": "", - "family": "code_pattern_php", - "tags": [ - "sast", - "php", - "php_v7.4.9" - ], - "instances": [ - "./1_instance_25_clone/1_instance_25_clone.json" - ], - "version": "v0.draft" +{ + "name": "Clone", + "description": "./docs/description.md", + "family": "code_pattern_php", + "tags": [ + "sast", + "php", + "php_v7.4.9" + ], + "instances": [ + "./1_instance_25_clone/1_instance_25_clone.json" + ], + "version": "v1.0" } \ No newline at end of file diff --git a/PHP/25_clone/Pattern Clone object.md b/PHP/25_clone/Pattern Clone object.md deleted file mode 100644 index 05c96c2..0000000 --- a/PHP/25_clone/Pattern Clone object.md +++ /dev/null @@ -1,111 +0,0 @@ -# Pattern: Clone Object - -## Category - -Objects - -## Definition - -## Instances - -### Instance 1 - -- CATEGORY: S0 -- FEATURE vs INTERNAL API: FEATURE -- INPUT SANITIZERS: NO -- SOURCES AND SINKS: NO -- NEGATIVE TEST CASES: NO -- CODE: - -```php -b = $b; - } - public function baz() - { - // $b has the input, XSS Vulnerability - echo $this->b; - } -} -$b = $_GET["p1"]; -$ob1 = new Foo($b); -$ob2 = clone $ob1; -$ob2->baz(); -//$ob->baz(); -//array(new Foo($b), "baz")(); -``` - -- MEASUREMENT: - -| Tool | RIPS | phpSAFE | WAP | Progpilot | Comm_1 | Comm_2 | Correct | -| ------------- | ---- | ------- | ---- | --------- | ------- | --------- | ------- | -| Vulnerability | NO | NO | NO | YES | YES | NO | YES | -Measurements Date: 8 June 2021 - -- OPCODE: - -```bash -$_main: ; (lines=17, args=0, vars=3, tmps=9) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/26_clone/26_clone.php:1-19 -L0 (4): NOP -L1 (14): EXT_STMT -L2 (14): T3 = FETCH_R (global) string("_GET") -L3 (14): T4 = FETCH_DIM_R T3 string("p1") -L4 (14): ASSIGN CV0($b) T4 -L5 (15): EXT_STMT -L6 (15): V6 = NEW 1 string("Foo") -L7 (15): SEND_VAR_EX CV0($b) 1 -L8 (15): DO_FCALL -L9 (15): ASSIGN CV1($ob1) V6 -L10 (16): EXT_STMT -L11 (16): T9 = CLONE CV1($ob1) -L12 (16): ASSIGN CV2($ob2) T9 -L13 (17): EXT_STMT -L14 (17): INIT_METHOD_CALL 0 CV2($ob2) string("baz") -L15 (17): DO_FCALL -L16 (19): RETURN int(1) -LIVE RANGES: - 6: L7 - L9 (new) - -Foo::__construct: ; (lines=7, args=1, vars=1, tmps=1) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/26_clone/26_clone.php:5-7 -L0 (5): EXT_NOP -L1 (5): CV0($b) = RECV 1 -L2 (6): EXT_STMT -L3 (6): ASSIGN_OBJ THIS string("b") -L4 (6): OP_DATA CV0($b) -L5 (7): EXT_STMT -L6 (7): RETURN null - -Foo::baz: ; (lines=6, args=0, vars=0, tmps=1) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/26_clone/26_clone.php:8-12 -L0 (8): EXT_NOP -L1 (11): EXT_STMT -L2 (11): T0 = FETCH_OBJ_R THIS string("b") -L3 (11): ECHO T0 -L4 (12): EXT_STMT -L5 (12): RETURN null -``` - -- DISCOVERY: - -```bash -cpg.call(".*CLONE.*").location.l -``` - -- PRECONDITIONS: - 1. - -- TRANSFORMATION: - -``` - -``` - diff --git a/PHP/25_clone/README.md b/PHP/25_clone/README.md new file mode 100755 index 0000000..2f3db43 --- /dev/null +++ b/PHP/25_clone/README.md @@ -0,0 +1,136 @@ +[//]: # (This file is automatically generated. If you wish to make any changes, please use the JSON files and regenerate this file using the tpframework.) + +# Clone + +Tags: sast, php, php_v7.4.9 + +Version: v1.0 + +## Description + +In PHP, clone is used to create a copy of an existing object, and it can be used to create a new instance with the same properties and values as the original one. + +## Overview + +| Instances | has discovery rule | discovery method | rule successfull | +|---------------------------|----------------------|--------------------|--------------------| +| [1 Instance](#1-instance) | yes | joern | yes | + +## 1 Instance + +This instance demonstrates, that when you clone an object, both the cloned object and the original object have the same value. + +### Code + +```PHP +b = $b; + } + public function baz() { + // $b has the input, XSS Vulnerability + return $this->b; + } +} +$b = $_GET["p1"]; // source +$ob1 = new Foo($b); +$ob2 = clone $ob1; // tarpit +$a = $ob2->baz(); +echo $a; // sink +``` + +### Instance Properties + +| category | feature_vs_internal_api | input_sanitizer | negative_test_case | source_and_sink | +|------------|---------------------------|-------------------|----------------------|-------------------| +| S0 | FEATURE | no | no | no | + +
+ +More + +
+ + +### Compile + + +```bash +$_main: + ; (lines=14, args=0, vars=4, tmps=10) + ; (before optimizer) + ; /.../PHP/25_clone/1_instance_25_clone/1_instance_25_clone.php:1-16 + ; return [] RANGE[0..0] +0000 T4 = FETCH_R (global) string("_GET") +0001 T5 = FETCH_DIM_R T4 string("p1") +0002 ASSIGN CV0($b) T5 +0003 V7 = NEW 1 string("Foo") +0004 SEND_VAR_EX CV0($b) 1 +0005 DO_FCALL +0006 ASSIGN CV1($ob1) V7 +0007 T10 = CLONE CV1($ob1) +0008 ASSIGN CV2($ob2) T10 +0009 INIT_METHOD_CALL 0 CV2($ob2) string("baz") +0010 V12 = DO_FCALL +0011 ASSIGN CV3($a) V12 +0012 ECHO CV3($a) +0013 RETURN int(1) +LIVE RANGES: + 7: 0004 - 0006 (new) + +Foo::__construct: + ; (lines=4, args=1, vars=1, tmps=1) + ; (before optimizer) + ; /.../PHP/25_clone/1_instance_25_clone/1_instance_25_clone.php:4-6 + ; return [] RANGE[0..0] +0000 CV0($b) = RECV 1 +0001 ASSIGN_OBJ THIS string("b") +0002 OP_DATA CV0($b) +0003 RETURN null + +Foo::baz: + ; (lines=3, args=0, vars=0, tmps=1) + ; (before optimizer) + ; /.../PHP/25_clone/1_instance_25_clone/1_instance_25_clone.php:7-10 + ; return [] RANGE[0..0] +0000 T0 = FETCH_OBJ_R THIS string("b") +0001 RETURN T0 +0002 RETURN null +``` + +
+ +
+ + +### Discovery + + +The rule searches for the opcode `CLONE`. + +```scala +val x25 = (name, "25_clone_iall", cpg.call(".*CLONE.*").location.toJson); +``` + +| discovery method | expected accuracy | +|--------------------|---------------------| +| joern | Perfect | + +
+ +
+ + +### Measurement + + +| Tool | Comm_1 | Comm_2 | phpSAFE | Progpilot | RIPS | WAP | Ground Truth | +|-------------|----------|----------|-----------|-------------|--------|-------|----------------| +| 08 Jun 2021 | yes | no | no | yes | no | no | yes | +| 17 May 2023 | yes | yes | | | | | yes | + +
+ +
diff --git a/PHP/25_clone/docs/description.md b/PHP/25_clone/docs/description.md new file mode 100755 index 0000000..9a29a24 --- /dev/null +++ b/PHP/25_clone/docs/description.md @@ -0,0 +1 @@ +In PHP, clone is used to create a copy of an existing object, and it can be used to create a new instance with the same properties and values as the original one. \ No newline at end of file diff --git a/PHP/26_late_static_binding/1_instance_26_late_static_binding/1_instance_26_late_static_binding.bash b/PHP/26_late_static_binding/1_instance_26_late_static_binding/1_instance_26_late_static_binding.bash old mode 100644 new mode 100755 index 3d77ba5..a8d689f --- a/PHP/26_late_static_binding/1_instance_26_late_static_binding/1_instance_26_late_static_binding.bash +++ b/PHP/26_late_static_binding/1_instance_26_late_static_binding/1_instance_26_late_static_binding.bash @@ -1,45 +1,45 @@ - -$_main: ; (lines=9, args=0, vars=1, tmps=4) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/36_late_static_binding/36_late_static_binding.php:1-18 -L0 (17): EXT_STMT -L1 (17): T1 = FETCH_R (global) string("_GET") -L2 (17): T2 = FETCH_DIM_R T1 string("p1") -L3 (17): ASSIGN CV0($b) T2 -L4 (18): EXT_STMT -L5 (18): INIT_STATIC_METHOD_CALL 1 string("B") string("test") -L6 (18): SEND_VAR CV0($b) 1 -L7 (18): DO_FCALL -L8 (18): RETURN int(1) - -A::who: ; (lines=6, args=1, vars=1, tmps=0) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/36_late_static_binding/36_late_static_binding.php:3-5 -L0 (3): EXT_NOP -L1 (3): CV0($b) = RECV 1 -L2 (4): EXT_STMT -L3 (4): ECHO string("safe") -L4 (5): EXT_STMT -L5 (5): RETURN null - -A::test: ; (lines=8, args=1, vars=1, tmps=1) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/36_late_static_binding/36_late_static_binding.php:6-8 -L0 (6): EXT_NOP -L1 (6): CV0($b) = RECV 1 -L2 (7): EXT_STMT -L3 (7): INIT_STATIC_METHOD_CALL 1 (static) (exception) string("who") -L4 (7): SEND_VAR_EX CV0($b) 1 -L5 (7): DO_FCALL -L6 (8): EXT_STMT -L7 (8): RETURN null - -B::who: ; (lines=6, args=1, vars=1, tmps=0) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/36_late_static_binding/36_late_static_binding.php:12-14 -L0 (12): EXT_NOP -L1 (12): CV0($b) = RECV 1 -L2 (13): EXT_STMT -L3 (13): ECHO CV0($b) -L4 (14): EXT_STMT -L5 (14): RETURN null + +$_main: + ; (lines=9, args=0, vars=2, tmps=5) + ; (before optimizer) + ; /.../PHP/26_late_static_binding/1_instance_26_late_static_binding/1_instance_26_late_static_binding.php:1-19 + ; return [] RANGE[0..0] +0000 T2 = FETCH_R (global) string("_GET") +0001 T3 = FETCH_DIM_R T2 string("p1") +0002 ASSIGN CV0($b) T3 +0003 INIT_STATIC_METHOD_CALL 1 string("B") string("test") +0004 SEND_VAR CV0($b) 1 +0005 V5 = DO_UCALL +0006 ASSIGN CV1($a) V5 +0007 ECHO CV1($a) +0008 RETURN int(1) + +A::who: + ; (lines=3, args=1, vars=1, tmps=0) + ; (before optimizer) + ; /.../PHP/26_late_static_binding/1_instance_26_late_static_binding/1_instance_26_late_static_binding.php:3-5 + ; return [] RANGE[0..0] +0000 CV0($b) = RECV 1 +0001 RETURN string("safe") +0002 RETURN null + +A::test: + ; (lines=6, args=1, vars=1, tmps=1) + ; (before optimizer) + ; /.../PHP/26_late_static_binding/1_instance_26_late_static_binding/1_instance_26_late_static_binding.php:6-8 + ; return [] RANGE[0..0] +0000 CV0($b) = RECV 1 +0001 INIT_STATIC_METHOD_CALL 1 (static) (exception) string("who") +0002 SEND_VAR_EX CV0($b) 1 +0003 V1 = DO_FCALL +0004 RETURN V1 +0005 RETURN null + +B::who: + ; (lines=3, args=1, vars=1, tmps=0) + ; (before optimizer) + ; /.../PHP/26_late_static_binding/1_instance_26_late_static_binding/1_instance_26_late_static_binding.php:12-14 + ; return [] RANGE[0..0] +0000 CV0($b) = RECV 1 +0001 RETURN CV0($b) +0002 RETURN null diff --git a/PHP/26_late_static_binding/1_instance_26_late_static_binding/1_instance_26_late_static_binding.json b/PHP/26_late_static_binding/1_instance_26_late_static_binding/1_instance_26_late_static_binding.json old mode 100644 new mode 100755 index 88c3b0f..9f48f65 --- a/PHP/26_late_static_binding/1_instance_26_late_static_binding/1_instance_26_late_static_binding.json +++ b/PHP/26_late_static_binding/1_instance_26_late_static_binding/1_instance_26_late_static_binding.json @@ -1,37 +1,38 @@ -{ - "code": { - "path": "./1_instance_26_late_static_binding.php", - "injection_skeleton_broken": false - }, - "discovery": { - "rule": "../26_late_static_binding.sc", - "method": "joern", - "rule_accuracy": null, - "notes": null - }, - "compile": { - "binary": "./1_instance_26_late_static_binding.bash", - "instruction": null, - "dependencies": null - }, - "expectation": { - "type": "xss", - "sink_file": "./1_instance_26_late_static_binding.php", - "sink_line": 13, - "source_file": "./1_instance_26_late_static_binding.php", - "source_line": 17, - "expectation": true - }, - "properties": { - "category": "D2", - "feature_vs_internal_api": "FEATURE", - "input_sanitizer": false, - "source_and_sink": false, - "negative_test_case": false - }, - "remediation": { - "notes": "", - "transformation": null, - "modeling_rule": null - } +{ + "description": "This instance binds the method `test` to the class `B`. This is why the function `who` from class `B` will be called, which is vulnerable.", + "code": { + "path": "./1_instance_26_late_static_binding.php", + "injection_skeleton_broken": false + }, + "discovery": { + "rule": "../26_late_static_binding.sc", + "method": "joern", + "rule_accuracy": "FP", + "notes": "The rule searches for an `INIT_STATIC_METHOD_CALL`, where the first argument is static." + }, + "compile": { + "binary": "./1_instance_26_late_static_binding.bash", + "instruction": null, + "dependencies": null + }, + "expectation": { + "type": "xss", + "sink_file": "./1_instance_26_late_static_binding.php", + "sink_line": 19, + "source_file": "./1_instance_26_late_static_binding.php", + "source_line": 17, + "expectation": true + }, + "properties": { + "category": "D2", + "feature_vs_internal_api": "FEATURE", + "input_sanitizer": false, + "source_and_sink": false, + "negative_test_case": false + }, + "remediation": { + "notes": "", + "transformation": null, + "modeling_rule": null + } } \ No newline at end of file diff --git a/PHP/26_late_static_binding/1_instance_26_late_static_binding/1_instance_26_late_static_binding.php b/PHP/26_late_static_binding/1_instance_26_late_static_binding/1_instance_26_late_static_binding.php old mode 100644 new mode 100755 index 40574a3..ed8d789 --- a/PHP/26_late_static_binding/1_instance_26_late_static_binding/1_instance_26_late_static_binding.php +++ b/PHP/26_late_static_binding/1_instance_26_late_static_binding/1_instance_26_late_static_binding.php @@ -1,18 +1,19 @@ - + + +## 1 Instance + + +This instance binds the method `test` to the class `B`. This is why the function `who` from class `B` will be called, which is vulnerable. + +### Code + +```PHP + + +More + +
+ + +### Compile + + +```bash +$_main: + ; (lines=9, args=0, vars=2, tmps=5) + ; (before optimizer) + ; /.../PHP/26_late_static_binding/1_instance_26_late_static_binding/1_instance_26_late_static_binding.php:1-19 + ; return [] RANGE[0..0] +0000 T2 = FETCH_R (global) string("_GET") +0001 T3 = FETCH_DIM_R T2 string("p1") +0002 ASSIGN CV0($b) T3 +0003 INIT_STATIC_METHOD_CALL 1 string("B") string("test") +0004 SEND_VAR CV0($b) 1 +0005 V5 = DO_UCALL +0006 ASSIGN CV1($a) V5 +0007 ECHO CV1($a) +0008 RETURN int(1) + +A::who: + ; (lines=3, args=1, vars=1, tmps=0) + ; (before optimizer) + ; /.../PHP/26_late_static_binding/1_instance_26_late_static_binding/1_instance_26_late_static_binding.php:3-5 + ; return [] RANGE[0..0] +0000 CV0($b) = RECV 1 +0001 RETURN string("safe") +0002 RETURN null + +A::test: + ; (lines=6, args=1, vars=1, tmps=1) + ; (before optimizer) + ; /.../PHP/26_late_static_binding/1_instance_26_late_static_binding/1_instance_26_late_static_binding.php:6-8 + ; return [] RANGE[0..0] +0000 CV0($b) = RECV 1 +0001 INIT_STATIC_METHOD_CALL 1 (static) (exception) string("who") +0002 SEND_VAR_EX CV0($b) 1 +0003 V1 = DO_FCALL +0004 RETURN V1 +0005 RETURN null + +B::who: + ; (lines=3, args=1, vars=1, tmps=0) + ; (before optimizer) + ; /.../PHP/26_late_static_binding/1_instance_26_late_static_binding/1_instance_26_late_static_binding.php:12-14 + ; return [] RANGE[0..0] +0000 CV0($b) = RECV 1 +0001 RETURN CV0($b) +0002 RETURN null +``` + +
+ +
+ + +### Discovery + + +The rule searches for an `INIT_STATIC_METHOD_CALL`, where the first argument is static. + +```scala +val x26 = (name, "26_late_static_binding_iall", cpg.call(".*INIT_STATIC_METHOD_CALL.*").argument.order(1).code("static").astParent.location.toJson); +``` + +| discovery method | expected accuracy | +|--------------------|---------------------| +| joern | FP | + +
+ +
+ + +### Measurement + + +| Tool | Comm_1 | Comm_2 | phpSAFE | Progpilot | RIPS | WAP | Ground Truth | +|-------------|----------|----------|-----------|-------------|--------|-------|----------------| +| 08 Jun 2021 | yes | no | no | no | no | no | yes | +| 17 May 2023 | yes | yes | | | | | yes | + +
+ + + + + +
+ + +## 2 Instance + + +This instance binds the method `test` to the class `A`. This is why the function `who` from class `A` will be called, which is returns 'save'. The instance is not vulnerable. + +### Code + +```PHP + + +More + +
+ + +### Compile + + +```bash +$_main: + ; (lines=9, args=0, vars=2, tmps=5) + ; (before optimizer) + ; /.../PHP/26_late_static_binding/2_instance_26_late_static_binding/2_instance_26_late_static_binding.php:1-19 + ; return [] RANGE[0..0] +0000 T2 = FETCH_R (global) string("_GET") +0001 T3 = FETCH_DIM_R T2 string("p1") +0002 ASSIGN CV0($b) T3 +0003 INIT_STATIC_METHOD_CALL 1 string("A") string("test") +0004 SEND_VAR CV0($b) 1 +0005 V5 = DO_UCALL +0006 ASSIGN CV1($a) V5 +0007 ECHO CV1($a) +0008 RETURN int(1) + +A::who: + ; (lines=3, args=1, vars=1, tmps=0) + ; (before optimizer) + ; /.../PHP/26_late_static_binding/2_instance_26_late_static_binding/2_instance_26_late_static_binding.php:3-5 + ; return [] RANGE[0..0] +0000 CV0($b) = RECV 1 +0001 RETURN string("safe") +0002 RETURN null + +A::test: + ; (lines=6, args=1, vars=1, tmps=1) + ; (before optimizer) + ; /.../PHP/26_late_static_binding/2_instance_26_late_static_binding/2_instance_26_late_static_binding.php:6-8 + ; return [] RANGE[0..0] +0000 CV0($b) = RECV 1 +0001 INIT_STATIC_METHOD_CALL 1 (static) (exception) string("who") +0002 SEND_VAR_EX CV0($b) 1 +0003 V1 = DO_FCALL +0004 RETURN V1 +0005 RETURN null + +B::who: + ; (lines=3, args=1, vars=1, tmps=0) + ; (before optimizer) + ; /.../PHP/26_late_static_binding/2_instance_26_late_static_binding/2_instance_26_late_static_binding.php:12-14 + ; return [] RANGE[0..0] +0000 CV0($b) = RECV 1 +0001 RETURN CV0($b) +0002 RETURN null +``` + +
+ +
+ + +### Discovery + + +The rule searches for an `INIT_STATIC_METHOD_CALL`, where the first argument is static. + +```scala +val x26 = (name, "26_late_static_binding_iall", cpg.call(".*INIT_STATIC_METHOD_CALL.*").argument.order(1).code("static").astParent.location.toJson); +``` + +| discovery method | expected accuracy | +|--------------------|---------------------| +| joern | FP | + +
+ +
+ + +### Measurement + + +| Tool | Comm_1 | Comm_2 | Ground Truth | +|-------------|----------|----------|----------------| +| 08 Jun 2021 | yes | | no | +| 17 May 2023 | yes | yes | no | + +
+ +
+ + diff --git a/PHP/26_late_static_binding/docs/description.md b/PHP/26_late_static_binding/docs/description.md new file mode 100755 index 0000000..08b8bc2 --- /dev/null +++ b/PHP/26_late_static_binding/docs/description.md @@ -0,0 +1 @@ +In PHP the keyword `self` does not follow inheritance. For example if you have a method defined in parent class and call it from the child class `self` will not resolve to the child class. To address this, [late static binding](https://www.php.net/manual/en/language.oop5.late-static-bindings.php) introduces a new use for the `static` keyword. When using `static` it binds the function to the class where it is first used. \ No newline at end of file diff --git a/PHP/27_get_called_class/1_instance_27_get_called_class/1_instance_27_get_called_class.bash b/PHP/27_get_called_class/1_instance_27_get_called_class/1_instance_27_get_called_class.bash old mode 100644 new mode 100755 index 89a475b..ddc9631 --- a/PHP/27_get_called_class/1_instance_27_get_called_class/1_instance_27_get_called_class.bash +++ b/PHP/27_get_called_class/1_instance_27_get_called_class/1_instance_27_get_called_class.bash @@ -1,47 +1,47 @@ - -$_main: ; (lines=9, args=0, vars=1, tmps=4) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/37_get_called_class/37_get_called_class.php:1-18 -L0 (17): EXT_STMT -L1 (17): T1 = FETCH_R (global) string("_GET") -L2 (17): T2 = FETCH_DIM_R T1 string("p1") -L3 (17): ASSIGN CV0($b) T2 -L4 (18): EXT_STMT -L5 (18): INIT_STATIC_METHOD_CALL 1 string("B") string("test") -L6 (18): SEND_VAR CV0($b) 1 -L7 (18): DO_FCALL -L8 (18): RETURN int(1) - -A::who: ; (lines=6, args=1, vars=1, tmps=0) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/37_get_called_class/37_get_called_class.php:3-5 -L0 (3): EXT_NOP -L1 (3): CV0($b) = RECV 1 -L2 (4): EXT_STMT -L3 (4): ECHO string("safe") -L4 (5): EXT_STMT -L5 (5): RETURN null - -A::test: ; (lines=10, args=1, vars=1, tmps=3) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/37_get_called_class/37_get_called_class.php:6-8 -L0 (6): EXT_NOP -L1 (6): CV0($b) = RECV 1 -L2 (7): EXT_STMT -L3 (7): T1 = GET_CALLED_CLASS -L4 (7): V2 = FETCH_CLASS (exception) T1 -L5 (7): INIT_STATIC_METHOD_CALL 1 V2 string("who") -L6 (7): SEND_VAR_EX CV0($b) 1 -L7 (7): DO_FCALL -L8 (8): EXT_STMT -L9 (8): RETURN null - -B::who: ; (lines=6, args=1, vars=1, tmps=0) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/37_get_called_class/37_get_called_class.php:12-14 -L0 (12): EXT_NOP -L1 (12): CV0($b) = RECV 1 -L2 (13): EXT_STMT -L3 (13): ECHO CV0($b) -L4 (14): EXT_STMT -L5 (14): RETURN null + +$_main: + ; (lines=9, args=0, vars=2, tmps=5) + ; (before optimizer) + ; /.../PHP/27_get_called_class/1_instance_27_get_called_class/1_instance_27_get_called_class.php:1-19 + ; return [] RANGE[0..0] +0000 T2 = FETCH_R (global) string("_GET") +0001 T3 = FETCH_DIM_R T2 string("p1") +0002 ASSIGN CV0($b) T3 +0003 INIT_STATIC_METHOD_CALL 1 string("B") string("test") +0004 SEND_VAR CV0($b) 1 +0005 V5 = DO_UCALL +0006 ASSIGN CV1($a) V5 +0007 ECHO CV1($a) +0008 RETURN int(1) + +A::who: + ; (lines=3, args=1, vars=1, tmps=0) + ; (before optimizer) + ; /.../PHP/27_get_called_class/1_instance_27_get_called_class/1_instance_27_get_called_class.php:3-5 + ; return [] RANGE[0..0] +0000 CV0($b) = RECV 1 +0001 RETURN string("safe") +0002 RETURN null + +A::test: + ; (lines=8, args=1, vars=1, tmps=3) + ; (before optimizer) + ; /.../PHP/27_get_called_class/1_instance_27_get_called_class/1_instance_27_get_called_class.php:6-8 + ; return [] RANGE[0..0] +0000 CV0($b) = RECV 1 +0001 T1 = GET_CALLED_CLASS +0002 V2 = FETCH_CLASS (exception) T1 +0003 INIT_STATIC_METHOD_CALL 1 V2 string("who") +0004 SEND_VAR_EX CV0($b) 1 +0005 V3 = DO_FCALL +0006 RETURN V3 +0007 RETURN null + +B::who: + ; (lines=3, args=1, vars=1, tmps=0) + ; (before optimizer) + ; /.../PHP/27_get_called_class/1_instance_27_get_called_class/1_instance_27_get_called_class.php:12-14 + ; return [] RANGE[0..0] +0000 CV0($b) = RECV 1 +0001 RETURN CV0($b) +0002 RETURN null diff --git a/PHP/27_get_called_class/1_instance_27_get_called_class/1_instance_27_get_called_class.json b/PHP/27_get_called_class/1_instance_27_get_called_class/1_instance_27_get_called_class.json old mode 100644 new mode 100755 index f3576df..5180f47 --- a/PHP/27_get_called_class/1_instance_27_get_called_class/1_instance_27_get_called_class.json +++ b/PHP/27_get_called_class/1_instance_27_get_called_class/1_instance_27_get_called_class.json @@ -1,37 +1,38 @@ -{ - "code": { - "path": "./1_instance_27_get_called_class.php", - "injection_skeleton_broken": false - }, - "discovery": { - "rule": "./1_instance_27_get_called_class.sc", - "method": "joern", - "rule_accuracy": "Perfect", - "notes": null - }, - "compile": { - "binary": "./1_instance_27_get_called_class.bash", - "instruction": null, - "dependencies": null - }, - "expectation": { - "type": "xss", - "sink_file": "./1_instance_27_get_called_class.php", - "sink_line": 13, - "source_file": "./1_instance_27_get_called_class.php", - "source_line": 17, - "expectation": true - }, - "properties": { - "category": "D2", - "feature_vs_internal_api": "FEATURE", - "input_sanitizer": false, - "source_and_sink": false, - "negative_test_case": false - }, - "remediation": { - "notes": "", - "transformation": null, - "modeling_rule": null - } +{ + "description": "The instance demonstrates how you can get the class from the object, that called the static method.", + "code": { + "path": "./1_instance_27_get_called_class.php", + "injection_skeleton_broken": false + }, + "discovery": { + "rule": "./1_instance_27_get_called_class.sc", + "method": "joern", + "rule_accuracy": "Perfect", + "notes": "The rule searches for the opcode `GET_CALLED_CLASS`." + }, + "compile": { + "binary": "./1_instance_27_get_called_class.bash", + "instruction": null, + "dependencies": null + }, + "expectation": { + "type": "xss", + "sink_file": "./1_instance_27_get_called_class.php", + "sink_line": 19, + "source_file": "./1_instance_27_get_called_class.php", + "source_line": 17, + "expectation": true + }, + "properties": { + "category": "D2", + "feature_vs_internal_api": "FEATURE", + "input_sanitizer": false, + "source_and_sink": false, + "negative_test_case": false + }, + "remediation": { + "notes": "", + "transformation": null, + "modeling_rule": null + } } \ No newline at end of file diff --git a/PHP/27_get_called_class/1_instance_27_get_called_class/1_instance_27_get_called_class.php b/PHP/27_get_called_class/1_instance_27_get_called_class/1_instance_27_get_called_class.php old mode 100644 new mode 100755 index fea0bb4..88363ca --- a/PHP/27_get_called_class/1_instance_27_get_called_class/1_instance_27_get_called_class.php +++ b/PHP/27_get_called_class/1_instance_27_get_called_class/1_instance_27_get_called_class.php @@ -1,18 +1,19 @@ - + +More + +
+ + +### Compile + + +```bash +$_main: + ; (lines=9, args=0, vars=2, tmps=5) + ; (before optimizer) + ; /.../PHP/27_get_called_class/1_instance_27_get_called_class/1_instance_27_get_called_class.php:1-19 + ; return [] RANGE[0..0] +0000 T2 = FETCH_R (global) string("_GET") +0001 T3 = FETCH_DIM_R T2 string("p1") +0002 ASSIGN CV0($b) T3 +0003 INIT_STATIC_METHOD_CALL 1 string("B") string("test") +0004 SEND_VAR CV0($b) 1 +0005 V5 = DO_UCALL +0006 ASSIGN CV1($a) V5 +0007 ECHO CV1($a) +0008 RETURN int(1) + +A::who: + ; (lines=3, args=1, vars=1, tmps=0) + ; (before optimizer) + ; /.../PHP/27_get_called_class/1_instance_27_get_called_class/1_instance_27_get_called_class.php:3-5 + ; return [] RANGE[0..0] +0000 CV0($b) = RECV 1 +0001 RETURN string("safe") +0002 RETURN null + +A::test: + ; (lines=8, args=1, vars=1, tmps=3) + ; (before optimizer) + ; /.../PHP/27_get_called_class/1_instance_27_get_called_class/1_instance_27_get_called_class.php:6-8 + ; return [] RANGE[0..0] +0000 CV0($b) = RECV 1 +0001 T1 = GET_CALLED_CLASS +0002 V2 = FETCH_CLASS (exception) T1 +0003 INIT_STATIC_METHOD_CALL 1 V2 string("who") +0004 SEND_VAR_EX CV0($b) 1 +0005 V3 = DO_FCALL +0006 RETURN V3 +0007 RETURN null + +B::who: + ; (lines=3, args=1, vars=1, tmps=0) + ; (before optimizer) + ; /.../PHP/27_get_called_class/1_instance_27_get_called_class/1_instance_27_get_called_class.php:12-14 + ; return [] RANGE[0..0] +0000 CV0($b) = RECV 1 +0001 RETURN CV0($b) +0002 RETURN null +``` + +
+ +
+ + +### Discovery + + +The rule searches for the opcode `GET_CALLED_CLASS`. + +```scala +val x27 = (name, "27_get_called_class_iall", cpg.call(".*GET_CALLED_CLASS.*").location.toJson); +``` + +| discovery method | expected accuracy | +|--------------------|---------------------| +| joern | Perfect | + +
+ +
+ + +### Measurement + + +| Tool | Comm_1 | Comm_2 | phpSAFE | Progpilot | RIPS | WAP | Ground Truth | +|-------------|----------|----------|-----------|-------------|--------|-------|----------------| +| 08 Jun 2021 | no | no | no | no | no | no | yes | +| 17 May 2023 | no | | | | | | yes | + +
+ + diff --git a/PHP/27_get_called_class/docs/description.md b/PHP/27_get_called_class/docs/description.md new file mode 100755 index 0000000..3a9c7e8 --- /dev/null +++ b/PHP/27_get_called_class/docs/description.md @@ -0,0 +1 @@ +In PHP `get_called_class` gets the class the static method is called in. \ No newline at end of file diff --git a/PHP/28_static_methods/1_instance_28_static_methods/1_instance_28_static_methods.bash b/PHP/28_static_methods/1_instance_28_static_methods/1_instance_28_static_methods.bash old mode 100644 new mode 100755 index f463ea5..f4e2abd --- a/PHP/28_static_methods/1_instance_28_static_methods/1_instance_28_static_methods.bash +++ b/PHP/28_static_methods/1_instance_28_static_methods/1_instance_28_static_methods.bash @@ -1,36 +1,34 @@ - -$_main: ; (lines=11, args=0, vars=0, tmps=4) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/46_static_methods/46_static_methods.php:1-16 -L0 (4): NOP -L1 (14): EXT_STMT -L2 (14): T1 = FETCH_R (global) string("_GET") -L3 (14): T2 = FETCH_DIM_R T1 string("p1") -L4 (14): ASSIGN_STATIC_PROP string("b") string("Foo") -L5 (14): OP_DATA T2 -L6 (15): EXT_STMT -L7 (15): INIT_STATIC_METHOD_CALL 0 string("Foo") string("baz") -L8 (15): V3 = DO_FCALL -L9 (15): ECHO V3 -L10 (16): RETURN int(1) - -Foo::__construct: ; (lines=7, args=1, vars=1, tmps=1) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/46_static_methods/46_static_methods.php:5-7 -L0 (5): EXT_NOP -L1 (5): CV0($b) = RECV 1 -L2 (6): EXT_STMT -L3 (6): ASSIGN_STATIC_PROP string("b") -L4 (6): OP_DATA CV0($b) -L5 (7): EXT_STMT -L6 (7): RETURN null - -Foo::baz: ; (lines=6, args=0, vars=0, tmps=1) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/46_static_methods/46_static_methods.php:8-12 -L0 (8): EXT_NOP -L1 (11): EXT_STMT -L2 (11): T0 = FETCH_STATIC_PROP_R string("b") (self) (exception) -L3 (11): ECHO T0 -L4 (12): EXT_STMT -L5 (12): RETURN null + +$_main: + ; (lines=9, args=0, vars=1, tmps=5) + ; (before optimizer) + ; /.../PHP/28_static_methods/1_instance_28_static_methods/1_instance_28_static_methods.php:1-15 + ; return [] RANGE[0..0] +0000 T2 = FETCH_R (global) string("_GET") +0001 T3 = FETCH_DIM_R T2 string("p1") +0002 ASSIGN_STATIC_PROP string("b") string("Foo") +0003 OP_DATA T3 +0004 INIT_STATIC_METHOD_CALL 0 string("Foo") string("baz") +0005 V4 = DO_UCALL +0006 ASSIGN CV0($a) V4 +0007 ECHO CV0($a) +0008 RETURN int(1) + +Foo::__construct: + ; (lines=4, args=1, vars=1, tmps=1) + ; (before optimizer) + ; /.../PHP/28_static_methods/1_instance_28_static_methods/1_instance_28_static_methods.php:4-6 + ; return [] RANGE[0..0] +0000 CV0($b) = RECV 1 +0001 ASSIGN_STATIC_PROP string("b") +0002 OP_DATA CV0($b) +0003 RETURN null + +Foo::baz: + ; (lines=3, args=0, vars=0, tmps=1) + ; (before optimizer) + ; /.../PHP/28_static_methods/1_instance_28_static_methods/1_instance_28_static_methods.php:7-10 + ; return [] RANGE[0..0] +0000 T0 = FETCH_STATIC_PROP_R string("b") (self) (exception) +0001 RETURN T0 +0002 RETURN null diff --git a/PHP/28_static_methods/1_instance_28_static_methods/1_instance_28_static_methods.json b/PHP/28_static_methods/1_instance_28_static_methods/1_instance_28_static_methods.json old mode 100644 new mode 100755 index f5ece33..5b47b45 --- a/PHP/28_static_methods/1_instance_28_static_methods/1_instance_28_static_methods.json +++ b/PHP/28_static_methods/1_instance_28_static_methods/1_instance_28_static_methods.json @@ -1,37 +1,38 @@ -{ - "code": { - "path": "./1_instance_28_static_methods.php", - "injection_skeleton_broken": false - }, - "discovery": { - "rule": "./1_instance_28_static_methods.sc", - "method": "joern", - "rule_accuracy": null, - "notes": null - }, - "compile": { - "binary": "./1_instance_28_static_methods.bash", - "instruction": null, - "dependencies": null - }, - "expectation": { - "type": "xss", - "sink_file": "./1_instance_28_static_methods.php", - "sink_line": 11, - "source_file": "./1_instance_28_static_methods.php", - "source_line": 14, - "expectation": true - }, - "properties": { - "category": "S0", - "feature_vs_internal_api": "FEATURE", - "input_sanitizer": false, - "source_and_sink": false, - "negative_test_case": false - }, - "remediation": { - "notes": "", - "transformation": null, - "modeling_rule": null - } +{ + "description": "The instance demonstrates the use of a static function in PHP.", + "code": { + "path": "./1_instance_28_static_methods.php", + "injection_skeleton_broken": false + }, + "discovery": { + "rule": "./1_instance_28_static_methods.sc", + "method": "joern", + "rule_accuracy": "FP", + "notes": "The rule searches for `INIT_STATIC_METHOD_CALL` in the opcode." + }, + "compile": { + "binary": "./1_instance_28_static_methods.bash", + "instruction": null, + "dependencies": null + }, + "expectation": { + "type": "xss", + "sink_file": "./1_instance_28_static_methods.php", + "sink_line": 14, + "source_file": "./1_instance_28_static_methods.php", + "source_line": 12, + "expectation": true + }, + "properties": { + "category": "S0", + "feature_vs_internal_api": "FEATURE", + "input_sanitizer": false, + "source_and_sink": false, + "negative_test_case": false + }, + "remediation": { + "notes": "", + "transformation": null, + "modeling_rule": null + } } \ No newline at end of file diff --git a/PHP/28_static_methods/1_instance_28_static_methods/1_instance_28_static_methods.php b/PHP/28_static_methods/1_instance_28_static_methods/1_instance_28_static_methods.php old mode 100644 new mode 100755 index 6e1ae20..bac2ce1 --- a/PHP/28_static_methods/1_instance_28_static_methods/1_instance_28_static_methods.php +++ b/PHP/28_static_methods/1_instance_28_static_methods/1_instance_28_static_methods.php @@ -1,15 +1,14 @@ - + +More + +
+ + +### Compile + + +```bash +$_main: + ; (lines=9, args=0, vars=1, tmps=5) + ; (before optimizer) + ; /.../PHP/28_static_methods/1_instance_28_static_methods/1_instance_28_static_methods.php:1-15 + ; return [] RANGE[0..0] +0000 T2 = FETCH_R (global) string("_GET") +0001 T3 = FETCH_DIM_R T2 string("p1") +0002 ASSIGN_STATIC_PROP string("b") string("Foo") +0003 OP_DATA T3 +0004 INIT_STATIC_METHOD_CALL 0 string("Foo") string("baz") +0005 V4 = DO_UCALL +0006 ASSIGN CV0($a) V4 +0007 ECHO CV0($a) +0008 RETURN int(1) + +Foo::__construct: + ; (lines=4, args=1, vars=1, tmps=1) + ; (before optimizer) + ; /.../PHP/28_static_methods/1_instance_28_static_methods/1_instance_28_static_methods.php:4-6 + ; return [] RANGE[0..0] +0000 CV0($b) = RECV 1 +0001 ASSIGN_STATIC_PROP string("b") +0002 OP_DATA CV0($b) +0003 RETURN null + +Foo::baz: + ; (lines=3, args=0, vars=0, tmps=1) + ; (before optimizer) + ; /.../PHP/28_static_methods/1_instance_28_static_methods/1_instance_28_static_methods.php:7-10 + ; return [] RANGE[0..0] +0000 T0 = FETCH_STATIC_PROP_R string("b") (self) (exception) +0001 RETURN T0 +0002 RETURN null +``` + +
+ +
+ + +### Discovery + + +The rule searches for `INIT_STATIC_METHOD_CALL` in the opcode. + +```scala +val x28 = (name, "28_static_methods_iall", cpg.call(".*INIT_STATIC_METHOD_CALL.*").location.toJson); +``` + +| discovery method | expected accuracy | +|--------------------|---------------------| +| joern | FP | + +
+ +
+ + +### Measurement + + +| Tool | Comm_1 | Comm_2 | phpSAFE | Progpilot | RIPS | WAP | Ground Truth | +|-------------|----------|----------|-----------|-------------|--------|-------|----------------| +| 08 Jun 2021 | yes | no | no | yes | no | no | yes | +| 17 May 2023 | yes | no | | | | | yes | + +
+ + diff --git a/PHP/28_static_methods/docs/description.md b/PHP/28_static_methods/docs/description.md new file mode 100755 index 0000000..3b428ac --- /dev/null +++ b/PHP/28_static_methods/docs/description.md @@ -0,0 +1 @@ +In PHP the Scope Resolution Operator (`::`) can be used to access static methods or variables. This pattern targets to capture static methods. \ No newline at end of file diff --git a/PHP/29_static_properties/1_instance_29_static_properties/1_instance_29_static_properties.bash b/PHP/29_static_properties/1_instance_29_static_properties/1_instance_29_static_properties.bash old mode 100644 new mode 100755 index aaf47d8..ebb6613 --- a/PHP/29_static_properties/1_instance_29_static_properties/1_instance_29_static_properties.bash +++ b/PHP/29_static_properties/1_instance_29_static_properties/1_instance_29_static_properties.bash @@ -1,41 +1,39 @@ - -$_main: ; (lines=14, args=0, vars=2, tmps=7) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/47_static_properties/first_ex/first_ex.php:1-16 -L0 (4): NOP -L1 (14): EXT_STMT -L2 (14): T2 = FETCH_R (global) string("_GET") -L3 (14): T3 = FETCH_DIM_R T2 string("p1") -L4 (14): ASSIGN CV0($b) T3 -L5 (15): EXT_STMT -L6 (15): V5 = NEW 1 string("Foo") -L7 (15): SEND_VAR_EX CV0($b) 1 -L8 (15): DO_FCALL -L9 (15): ASSIGN CV1($ob) V5 -L10 (16): EXT_STMT -L11 (16): INIT_METHOD_CALL 0 CV1($ob) string("baz") -L12 (16): DO_FCALL -L13 (16): RETURN int(1) -LIVE RANGES: - 5: L7 - L9 (new) - -Foo::__construct: ; (lines=7, args=1, vars=1, tmps=1) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/47_static_properties/first_ex/first_ex.php:5-7 -L0 (5): EXT_NOP -L1 (5): CV0($b) = RECV 1 -L2 (6): EXT_STMT -L3 (6): ASSIGN_STATIC_PROP string("b") -L4 (6): OP_DATA CV0($b) -L5 (7): EXT_STMT -L6 (7): RETURN null - -Foo::baz: ; (lines=6, args=0, vars=0, tmps=1) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/47_static_properties/first_ex/first_ex.php:8-12 -L0 (8): EXT_NOP -L1 (11): EXT_STMT -L2 (11): T0 = FETCH_STATIC_PROP_R string("b") (self) (exception) -L3 (11): ECHO T0 -L4 (12): EXT_STMT -L5 (12): RETURN null + +$_main: + ; (lines=12, args=0, vars=3, tmps=8) + ; (before optimizer) + ; /.../PHP/29_static_properties/1_instance_29_static_properties/1_instance_29_static_properties.php:1-15 + ; return [] RANGE[0..0] +0000 T3 = FETCH_R (global) string("_GET") +0001 T4 = FETCH_DIM_R T3 string("p1") +0002 ASSIGN CV0($b) T4 +0003 V6 = NEW 1 string("Foo") +0004 SEND_VAR_EX CV0($b) 1 +0005 DO_FCALL +0006 ASSIGN CV1($ob) V6 +0007 INIT_METHOD_CALL 0 CV1($ob) string("baz") +0008 V9 = DO_FCALL +0009 ASSIGN CV2($a) V9 +0010 ECHO CV2($a) +0011 RETURN int(1) +LIVE RANGES: + 6: 0004 - 0006 (new) + +Foo::__construct: + ; (lines=4, args=1, vars=1, tmps=1) + ; (before optimizer) + ; /.../PHP/29_static_properties/1_instance_29_static_properties/1_instance_29_static_properties.php:4-6 + ; return [] RANGE[0..0] +0000 CV0($b) = RECV 1 +0001 ASSIGN_STATIC_PROP string("b") +0002 OP_DATA CV0($b) +0003 RETURN null + +Foo::baz: + ; (lines=3, args=0, vars=0, tmps=1) + ; (before optimizer) + ; /.../PHP/29_static_properties/1_instance_29_static_properties/1_instance_29_static_properties.php:7-10 + ; return [] RANGE[0..0] +0000 T0 = FETCH_STATIC_PROP_R string("b") (self) (exception) +0001 RETURN T0 +0002 RETURN null diff --git a/PHP/29_static_properties/1_instance_29_static_properties/1_instance_29_static_properties.json b/PHP/29_static_properties/1_instance_29_static_properties/1_instance_29_static_properties.json old mode 100644 new mode 100755 index fdcad25..1bad920 --- a/PHP/29_static_properties/1_instance_29_static_properties/1_instance_29_static_properties.json +++ b/PHP/29_static_properties/1_instance_29_static_properties/1_instance_29_static_properties.json @@ -1,37 +1,38 @@ -{ - "code": { - "path": "./1_instance_29_static_properties.php", - "injection_skeleton_broken": false - }, - "discovery": { - "rule": "./1_instance_29_static_properties.sc", - "method": "joern", - "rule_accuracy": null, - "notes": null - }, - "compile": { - "binary": "./1_instance_29_static_properties.bash", - "instruction": null, - "dependencies": null - }, - "expectation": { - "type": "xss", - "sink_file": "./1_instance_29_static_properties.php", - "sink_line": 11, - "source_file": "./1_instance_29_static_properties.php", - "source_line": 14, - "expectation": true - }, - "properties": { - "category": "S0", - "feature_vs_internal_api": "FEATURE", - "input_sanitizer": false, - "source_and_sink": false, - "negative_test_case": false - }, - "remediation": { - "notes": "", - "transformation": null, - "modeling_rule": null - } +{ + "description": "This instance uses static properties.", + "code": { + "path": "./1_instance_29_static_properties.php", + "injection_skeleton_broken": false + }, + "discovery": { + "rule": "./1_instance_29_static_properties.sc", + "method": "joern", + "rule_accuracy": "FP", + "notes": "The rule searches for different posibilities one has to initialize static properties." + }, + "compile": { + "binary": "./1_instance_29_static_properties.bash", + "instruction": null, + "dependencies": null + }, + "expectation": { + "type": "xss", + "sink_file": "./1_instance_29_static_properties.php", + "sink_line": 15, + "source_file": "./1_instance_29_static_properties.php", + "source_line": 12, + "expectation": true + }, + "properties": { + "category": "S0", + "feature_vs_internal_api": "FEATURE", + "input_sanitizer": false, + "source_and_sink": false, + "negative_test_case": false + }, + "remediation": { + "notes": "", + "transformation": null, + "modeling_rule": null + } } \ No newline at end of file diff --git a/PHP/29_static_properties/1_instance_29_static_properties/1_instance_29_static_properties.php b/PHP/29_static_properties/1_instance_29_static_properties/1_instance_29_static_properties.php old mode 100644 new mode 100755 index d4015ea..429eab4 --- a/PHP/29_static_properties/1_instance_29_static_properties/1_instance_29_static_properties.php +++ b/PHP/29_static_properties/1_instance_29_static_properties/1_instance_29_static_properties.php @@ -1,16 +1,15 @@ -baz(); \ No newline at end of file +baz(); +echo $a; // sink \ No newline at end of file diff --git a/PHP/29_static_properties/1_instance_29_static_properties/1_instance_29_static_properties.sc b/PHP/29_static_properties/1_instance_29_static_properties/1_instance_29_static_properties.sc old mode 100644 new mode 100755 index 8b64665..35a176f --- a/PHP/29_static_properties/1_instance_29_static_properties/1_instance_29_static_properties.sc +++ b/PHP/29_static_properties/1_instance_29_static_properties/1_instance_29_static_properties.sc @@ -1,6 +1,6 @@ -@main def main(name : String): Unit = { - importCpg(name) - val x29 = (name, "29_static_properties_iall", cpg.call(".*ASSIGN_STATIC_PROP.*|.*FETCH_STATIC_PROP_R.*|.*FETCH_STATIC_PROP_W.*|.*FETCH_STATIC_PROP_RW.*").location.toJson); - println(x29) - delete; +@main def main(name : String): Unit = { + importCpg(name) + val x29 = (name, "29_static_properties_iall", cpg.call(".*ASSIGN_STATIC_PROP.*|.*FETCH_STATIC_PROP_R.*|.*FETCH_STATIC_PROP_W.*|.*FETCH_STATIC_PROP_RW.*").location.toJson); + println(x29) + delete; } \ No newline at end of file diff --git a/PHP/29_static_properties/29_static_properties.json b/PHP/29_static_properties/29_static_properties.json old mode 100644 new mode 100755 index 1163116..4fbcd06 --- a/PHP/29_static_properties/29_static_properties.json +++ b/PHP/29_static_properties/29_static_properties.json @@ -1,14 +1,14 @@ -{ - "name": "Static Properties", - "description": "", - "family": "code_pattern_php", - "tags": [ - "sast", - "php", - "php_v7.4.9" - ], - "instances": [ - "./1_instance_29_static_properties/1_instance_29_static_properties.json" - ], - "version": "v0.draft" +{ + "name": "Static Properties", + "description": "./docs/description.md", + "family": "code_pattern_php", + "tags": [ + "sast", + "php", + "php_v7.4.9" + ], + "instances": [ + "./1_instance_29_static_properties/1_instance_29_static_properties.json" + ], + "version": "v1.0" } \ No newline at end of file diff --git a/PHP/29_static_properties/Pattern Static Properties.md b/PHP/29_static_properties/Pattern Static Properties.md deleted file mode 100644 index 6fda4fc..0000000 --- a/PHP/29_static_properties/Pattern Static Properties.md +++ /dev/null @@ -1,104 +0,0 @@ -# Pattern: Static Properties - -## Category - -Objects - -## Definition - -## Instances - -### Instance 1 - -- CATEGORY: S0 -- FEATURE vs INTERNAL API: FEATURE -- INPUT SANITIZERS: NO -- SOURCES AND SINKS: NO -- NEGATIVE TEST CASES: NO -- CODE: - -```php -baz(); -``` - -- MEASUREMENT: - -| Tool | RIPS | phpSAFE | WAP | Progpilot | Comm_1 | Comm_2 | Correct | -| ------------- | ---- | ------- | ---- | --------- | ------- | --------- | ------- | -| Vulnerability |NO |NO | NO | YES | YES | NO | YES | -Measurements Date: 8 June 2021 - -- OPCODE: - -```bash -$_main: ; (lines=14, args=0, vars=2, tmps=7) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/47_static_properties/first_ex/first_ex.php:1-16 -L0 (4): NOP -L1 (14): EXT_STMT -L2 (14): T2 = FETCH_R (global) string("_GET") -L3 (14): T3 = FETCH_DIM_R T2 string("p1") -L4 (14): ASSIGN CV0($b) T3 -L5 (15): EXT_STMT -L6 (15): V5 = NEW 1 string("Foo") -L7 (15): SEND_VAR_EX CV0($b) 1 -L8 (15): DO_FCALL -L9 (15): ASSIGN CV1($ob) V5 -L10 (16): EXT_STMT -L11 (16): INIT_METHOD_CALL 0 CV1($ob) string("baz") -L12 (16): DO_FCALL -L13 (16): RETURN int(1) -LIVE RANGES: - 5: L7 - L9 (new) - -Foo::__construct: ; (lines=7, args=1, vars=1, tmps=1) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/47_static_properties/first_ex/first_ex.php:5-7 -L0 (5): EXT_NOP -L1 (5): CV0($b) = RECV 1 -L2 (6): EXT_STMT -L3 (6): ASSIGN_STATIC_PROP string("b") -L4 (6): OP_DATA CV0($b) -L5 (7): EXT_STMT -L6 (7): RETURN null - -Foo::baz: ; (lines=6, args=0, vars=0, tmps=1) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/47_static_properties/first_ex/first_ex.php:8-12 -L0 (8): EXT_NOP -L1 (11): EXT_STMT -L2 (11): T0 = FETCH_STATIC_PROP_R string("b") (self) (exception) -L3 (11): ECHO T0 -L4 (12): EXT_STMT -L5 (12): RETURN null -``` - -- DISCOVERY: - -```bash -cpg.call(".*ASSIGN_STATIC_PROP.*|.*FETCH_STATIC_PROP_R.*|.*FETCH_STATIC_PROP_W.*|.*FETCH_STATIC_PROP_RW.*").location.l -``` - -- PRECONDITIONS: - 1. - -- TRANSFORMATION: T2 - -``` - -``` diff --git a/PHP/29_static_properties/README.md b/PHP/29_static_properties/README.md new file mode 100755 index 0000000..a571320 --- /dev/null +++ b/PHP/29_static_properties/README.md @@ -0,0 +1,133 @@ +[//]: # (This file is automatically generated. If you wish to make any changes, please use the JSON files and regenerate this file using the tpframework.) + +# Static Properties + +Tags: sast, php, php_v7.4.9 + +Version: v1.0 + +## Description + +In PHP Scope Resolution Operator (`::`) can be used to access static methods or variables. This pattern targets to capture static variables. + +## Overview + +| Instances | has discovery rule | discovery method | rule successfull | +|---------------------------|----------------------|--------------------|--------------------| +| [1 Instance](#1-instance) | yes | joern | yes | + +## 1 Instance + +This instance uses static properties. + +### Code + +```PHP +baz(); +echo $a; // sink +``` + +### Instance Properties + +| category | feature_vs_internal_api | input_sanitizer | negative_test_case | source_and_sink | +|------------|---------------------------|-------------------|----------------------|-------------------| +| S0 | FEATURE | no | no | no | + +
+ +More + +
+ + +### Compile + + +```bash +$_main: + ; (lines=12, args=0, vars=3, tmps=8) + ; (before optimizer) + ; /.../PHP/29_static_properties/1_instance_29_static_properties/1_instance_29_static_properties.php:1-15 + ; return [] RANGE[0..0] +0000 T3 = FETCH_R (global) string("_GET") +0001 T4 = FETCH_DIM_R T3 string("p1") +0002 ASSIGN CV0($b) T4 +0003 V6 = NEW 1 string("Foo") +0004 SEND_VAR_EX CV0($b) 1 +0005 DO_FCALL +0006 ASSIGN CV1($ob) V6 +0007 INIT_METHOD_CALL 0 CV1($ob) string("baz") +0008 V9 = DO_FCALL +0009 ASSIGN CV2($a) V9 +0010 ECHO CV2($a) +0011 RETURN int(1) +LIVE RANGES: + 6: 0004 - 0006 (new) + +Foo::__construct: + ; (lines=4, args=1, vars=1, tmps=1) + ; (before optimizer) + ; /.../PHP/29_static_properties/1_instance_29_static_properties/1_instance_29_static_properties.php:4-6 + ; return [] RANGE[0..0] +0000 CV0($b) = RECV 1 +0001 ASSIGN_STATIC_PROP string("b") +0002 OP_DATA CV0($b) +0003 RETURN null + +Foo::baz: + ; (lines=3, args=0, vars=0, tmps=1) + ; (before optimizer) + ; /.../PHP/29_static_properties/1_instance_29_static_properties/1_instance_29_static_properties.php:7-10 + ; return [] RANGE[0..0] +0000 T0 = FETCH_STATIC_PROP_R string("b") (self) (exception) +0001 RETURN T0 +0002 RETURN null +``` + +
+ +
+ + +### Discovery + + +The rule searches for different posibilities one has to initialize static properties. + +```scala +val x29 = (name, "29_static_properties_iall", cpg.call(".*ASSIGN_STATIC_PROP.*|.*FETCH_STATIC_PROP_R.*|.*FETCH_STATIC_PROP_W.*|.*FETCH_STATIC_PROP_RW.*").location.toJson); +``` + +| discovery method | expected accuracy | +|--------------------|---------------------| +| joern | FP | + +
+ +
+ + +### Measurement + + +| Tool | Comm_1 | Comm_2 | phpSAFE | Progpilot | RIPS | WAP | Ground Truth | +|-------------|----------|----------|-----------|-------------|--------|-------|----------------| +| 08 Jun 2021 | yes | no | no | yes | no | no | yes | +| 17 May 2023 | yes | no | | | | | yes | + +
+ +
diff --git a/PHP/29_static_properties/docs/description.md b/PHP/29_static_properties/docs/description.md new file mode 100755 index 0000000..d7417b9 --- /dev/null +++ b/PHP/29_static_properties/docs/description.md @@ -0,0 +1 @@ +In PHP Scope Resolution Operator (`::`) can be used to access static methods or variables. This pattern targets to capture static variables. \ No newline at end of file diff --git a/PHP/2_global_variables/1_instance_2_global_variables/1_instance_2_global_variables.bash b/PHP/2_global_variables/1_instance_2_global_variables/1_instance_2_global_variables.bash old mode 100644 new mode 100755 index 873be5f..aa756c6 --- a/PHP/2_global_variables/1_instance_2_global_variables/1_instance_2_global_variables.bash +++ b/PHP/2_global_variables/1_instance_2_global_variables/1_instance_2_global_variables.bash @@ -1,29 +1,25 @@ - -$_main: ; (lines=13, args=0, vars=2, tmps=5) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/3_global_variables/3_global_variables.php:1-11 -L0 (2): EXT_STMT -L1 (2): ASSIGN CV0($result) string("") -L2 (8): EXT_STMT -L3 (8): T3 = FETCH_R (global) string("_GET") -L4 (8): T4 = FETCH_DIM_R T3 string("p1") -L5 (8): ASSIGN CV1($words) T4 -L6 (9): EXT_STMT -L7 (9): INIT_FCALL 1 128 string("f") -L8 (9): SEND_VAR CV1($words) 1 -L9 (9): DO_FCALL -L10 (11): EXT_STMT -L11 (11): ECHO CV0($result) -L12 (11): RETURN int(1) - -F: ; (lines=8, args=1, vars=2, tmps=1) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/3_global_variables/3_global_variables.php:3-7 -L0 (3): EXT_NOP -L1 (3): CV0($word) = RECV 1 -L2 (4): EXT_STMT -L3 (4): BIND_GLOBAL CV1($result) string("result") -L4 (6): EXT_STMT -L5 (6): ASSIGN CV1($result) CV0($word) -L6 (7): EXT_STMT -L7 (7): RETURN null + +$_main: + ; (lines=9, args=0, vars=2, tmps=5) + ; (before optimizer) + ; /.../PHP/2_global_variables/1_instance_2_global_variables/1_instance_2_global_variables.php:1-11 + ; return [] RANGE[0..0] +0000 ASSIGN CV0($result) string("") +0001 T3 = FETCH_R (global) string("_GET") +0002 T4 = FETCH_DIM_R T3 string("p1") +0003 ASSIGN CV1($words) T4 +0004 INIT_FCALL 1 128 string("f") +0005 SEND_VAR CV1($words) 1 +0006 DO_UCALL +0007 ECHO CV0($result) +0008 RETURN int(1) + +F: + ; (lines=4, args=1, vars=2, tmps=1) + ; (before optimizer) + ; /.../PHP/2_global_variables/1_instance_2_global_variables/1_instance_2_global_variables.php:3-7 + ; return [] RANGE[0..0] +0000 CV0($word) = RECV 1 +0001 BIND_GLOBAL CV1($result) string("result") +0002 ASSIGN CV1($result) CV0($word) +0003 RETURN null diff --git a/PHP/2_global_variables/1_instance_2_global_variables/1_instance_2_global_variables.json b/PHP/2_global_variables/1_instance_2_global_variables/1_instance_2_global_variables.json old mode 100644 new mode 100755 index 374f154..49678a3 --- a/PHP/2_global_variables/1_instance_2_global_variables/1_instance_2_global_variables.json +++ b/PHP/2_global_variables/1_instance_2_global_variables/1_instance_2_global_variables.json @@ -1,37 +1,38 @@ -{ - "code": { - "path": "./1_instance_2_global_variables.php", - "injection_skeleton_broken": false - }, - "discovery": { - "rule": "./1_instance_2_global_variables.sc", - "method": "joern", - "rule_accuracy": "FP", - "notes": "ideal rule should looking for `global` variables used in a function" - }, - "remediation": { - "notes": null, - "transformation": null, - "modeling_rule": null - }, - "compile": { - "binary": "./1_instance_2_global_variables.bash", - "dependencies": null, - "instruction": null - }, - "expectation": { - "type": "xss", - "sink_file": "./1_instance_2_global_variables.php", - "sink_line": 11, - "source_file": "./1_instance_2_global_variables.php", - "source_line": 8, - "expectation": true - }, - "properties": { - "category": "S0", - "feature_vs_internal_api": "FEATURE", - "input_sanitizer": false, - "source_and_sink": false, - "negative_test_case": false - } +{ + "description": "This instance should capture the use of global variables in a function.", + "code": { + "path": "./1_instance_2_global_variables.php", + "injection_skeleton_broken": false + }, + "discovery": { + "rule": "./1_instance_2_global_variables.sc", + "method": "joern", + "rule_accuracy": "FP", + "notes": "ideal rule should looking for `global` variables used in a function" + }, + "remediation": { + "notes": null, + "transformation": null, + "modeling_rule": null + }, + "compile": { + "binary": "./1_instance_2_global_variables.bash", + "dependencies": null, + "instruction": null + }, + "expectation": { + "type": "xss", + "sink_file": "./1_instance_2_global_variables.php", + "sink_line": 11, + "source_file": "./1_instance_2_global_variables.php", + "source_line": 8, + "expectation": true + }, + "properties": { + "category": "S0", + "feature_vs_internal_api": "FEATURE", + "input_sanitizer": false, + "source_and_sink": false, + "negative_test_case": false + } } \ No newline at end of file diff --git a/PHP/2_global_variables/1_instance_2_global_variables/1_instance_2_global_variables.php b/PHP/2_global_variables/1_instance_2_global_variables/1_instance_2_global_variables.php old mode 100644 new mode 100755 index 9afb8ce..14b9744 --- a/PHP/2_global_variables/1_instance_2_global_variables/1_instance_2_global_variables.php +++ b/PHP/2_global_variables/1_instance_2_global_variables/1_instance_2_global_variables.php @@ -1,11 +1,11 @@ - + +More + +
+ + +### Compile + + +```bash +$_main: + ; (lines=9, args=0, vars=2, tmps=5) + ; (before optimizer) + ; /.../PHP/2_global_variables/1_instance_2_global_variables/1_instance_2_global_variables.php:1-11 + ; return [] RANGE[0..0] +0000 ASSIGN CV0($result) string("") +0001 T3 = FETCH_R (global) string("_GET") +0002 T4 = FETCH_DIM_R T3 string("p1") +0003 ASSIGN CV1($words) T4 +0004 INIT_FCALL 1 128 string("f") +0005 SEND_VAR CV1($words) 1 +0006 DO_UCALL +0007 ECHO CV0($result) +0008 RETURN int(1) + +F: + ; (lines=4, args=1, vars=2, tmps=1) + ; (before optimizer) + ; /.../PHP/2_global_variables/1_instance_2_global_variables/1_instance_2_global_variables.php:3-7 + ; return [] RANGE[0..0] +0000 CV0($word) = RECV 1 +0001 BIND_GLOBAL CV1($result) string("result") +0002 ASSIGN CV1($result) CV0($word) +0003 RETURN null +``` + +
+ +
+ + +### Discovery + + +ideal rule should looking for `global` variables used in a function + +```scala +val x2 = (name, "2_global_variables_iall", cpg.call(".*BIND_GLOBAL.*").location.toJson); +``` + +| discovery method | expected accuracy | +|--------------------|---------------------| +| joern | FP | + +
+ +
+ + +### Measurement + + +| Tool | Comm_1 | Comm_2 | phpSAFE | Progpilot | RIPS | WAP | Ground Truth | +|-------------|----------|----------|-----------|-------------|--------|-------|----------------| +| 08 Jun 2021 | yes | yes | yes | no | no | no | yes | +| 17 May 2023 | yes | yes | | | | | yes | + +
+ + diff --git a/PHP/2_global_variables/docs/description.md b/PHP/2_global_variables/docs/description.md old mode 100644 new mode 100755 diff --git a/PHP/30_anonymous_classes/1_instance_30_anonymous_classes/1_instance_30_anonymous_classes.bash b/PHP/30_anonymous_classes/1_instance_30_anonymous_classes/1_instance_30_anonymous_classes.bash old mode 100644 new mode 100755 index 1c4d043..5586c58 --- a/PHP/30_anonymous_classes/1_instance_30_anonymous_classes/1_instance_30_anonymous_classes.bash +++ b/PHP/30_anonymous_classes/1_instance_30_anonymous_classes/1_instance_30_anonymous_classes.bash @@ -1,30 +1,30 @@ - -$_main: ; (lines=14, args=0, vars=2, tmps=8) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/68_anonymous_classes/68_anonymous_classes.php:1-10 -L0 (2): EXT_STMT -L1 (2): T2 = FETCH_R (global) string("_GET") -L2 (2): T3 = FETCH_DIM_R T2 string("p1") -L3 (2): ASSIGN CV0($b) T3 -L4 (3): EXT_STMT -L5 (3): V5 = DECLARE_ANON_CLASS string("class@anonymous") -L6 (3): V6 = NEW 0 V5 -L7 (3): DO_FCALL -L8 (3): ASSIGN CV1($util) V6 -L9 (10): EXT_STMT -L10 (10): INIT_METHOD_CALL 1 CV1($util) string("log") -L11 (10): SEND_VAR_EX CV0($b) 1 -L12 (10): DO_FCALL -L13 (10): RETURN int(1) -LIVE RANGES: - 6: L7 - L8 (new) - -class@anonymous::log: ; (lines=6, args=1, vars=1, tmps=0) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/68_anonymous_classes/68_anonymous_classes.php:4-7 -L0 (4): EXT_NOP -L1 (4): CV0($msg) = RECV 1 -L2 (6): EXT_STMT -L3 (6): ECHO CV0($msg) -L4 (7): EXT_STMT -L5 (7): RETURN null + +$_main: + ; (lines=13, args=0, vars=3, tmps=9) + ; (before optimizer) + ; /.../PHP/30_anonymous_classes/1_instance_30_anonymous_classes/1_instance_30_anonymous_classes.php:1-10 + ; return [] RANGE[0..0] +0000 T3 = FETCH_R (global) string("_GET") +0001 T4 = FETCH_DIM_R T3 string("p1") +0002 ASSIGN CV0($b) T4 +0003 V6 = DECLARE_ANON_CLASS string("class@anonymous") +0004 V7 = NEW 0 V6 +0005 DO_FCALL +0006 ASSIGN CV1($util) V7 +0007 INIT_METHOD_CALL 1 CV1($util) string("log") +0008 SEND_VAR_EX CV0($b) 1 +0009 V10 = DO_FCALL +0010 ASSIGN CV2($a) V10 +0011 ECHO CV2($a) +0012 RETURN int(1) +LIVE RANGES: + 7: 0005 - 0006 (new) + +class@anonymous::log: + ; (lines=3, args=1, vars=1, tmps=0) + ; (before optimizer) + ; /.../PHP/30_anonymous_classes/1_instance_30_anonymous_classes/1_instance_30_anonymous_classes.php:4-6 + ; return [] RANGE[0..0] +0000 CV0($msg) = RECV 1 +0001 RETURN CV0($msg) +0002 RETURN null diff --git a/PHP/30_anonymous_classes/1_instance_30_anonymous_classes/1_instance_30_anonymous_classes.json b/PHP/30_anonymous_classes/1_instance_30_anonymous_classes/1_instance_30_anonymous_classes.json old mode 100644 new mode 100755 index c3772f1..e6a9357 --- a/PHP/30_anonymous_classes/1_instance_30_anonymous_classes/1_instance_30_anonymous_classes.json +++ b/PHP/30_anonymous_classes/1_instance_30_anonymous_classes/1_instance_30_anonymous_classes.json @@ -1,37 +1,38 @@ -{ - "code": { - "path": "./1_instance_30_anonymous_classes.php", - "injection_skeleton_broken": false - }, - "discovery": { - "rule": "./1_instance_30_anonymous_classes.sc", - "method": "joern", - "rule_accuracy": null, - "notes": null - }, - "compile": { - "binary": "./1_instance_30_anonymous_classes.bash", - "instruction": null, - "dependencies": null - }, - "expectation": { - "type": "xss", - "sink_file": "./1_instance_30_anonymous_classes.php", - "sink_line": 6, - "source_file": "./1_instance_30_anonymous_classes.php", - "source_line": 2, - "expectation": true - }, - "properties": { - "category": "S0", - "feature_vs_internal_api": "FEATURE", - "input_sanitizer": false, - "source_and_sink": false, - "negative_test_case": false - }, - "remediation": { - "notes": "", - "transformation": null, - "modeling_rule": null - } +{ + "description": "This instance demonstrates the use of anonymous classes.", + "code": { + "path": "./1_instance_30_anonymous_classes.php", + "injection_skeleton_broken": false + }, + "discovery": { + "rule": "./1_instance_30_anonymous_classes.sc", + "method": "joern", + "rule_accuracy": "Perfect", + "notes": "The opecode searches for `DECLARE_ANON_CLASS` in the opcode." + }, + "compile": { + "binary": "./1_instance_30_anonymous_classes.bash", + "instruction": null, + "dependencies": null + }, + "expectation": { + "type": "xss", + "sink_file": "./1_instance_30_anonymous_classes.php", + "sink_line": 10, + "source_file": "./1_instance_30_anonymous_classes.php", + "source_line": 2, + "expectation": true + }, + "properties": { + "category": "S0", + "feature_vs_internal_api": "FEATURE", + "input_sanitizer": false, + "source_and_sink": false, + "negative_test_case": false + }, + "remediation": { + "notes": "If named classes are easier for SAST tools, it should be possible to transform an anonymous class into a named class.", + "transformation": null, + "modeling_rule": null + } } \ No newline at end of file diff --git a/PHP/30_anonymous_classes/1_instance_30_anonymous_classes/1_instance_30_anonymous_classes.php b/PHP/30_anonymous_classes/1_instance_30_anonymous_classes/1_instance_30_anonymous_classes.php old mode 100644 new mode 100755 index 33b8cf4..dacf54a --- a/PHP/30_anonymous_classes/1_instance_30_anonymous_classes/1_instance_30_anonymous_classes.php +++ b/PHP/30_anonymous_classes/1_instance_30_anonymous_classes/1_instance_30_anonymous_classes.php @@ -1,10 +1,10 @@ -log($b); \ No newline at end of file +log($b); +echo $a; // sink \ No newline at end of file diff --git a/PHP/30_anonymous_classes/1_instance_30_anonymous_classes/1_instance_30_anonymous_classes.sc b/PHP/30_anonymous_classes/1_instance_30_anonymous_classes/1_instance_30_anonymous_classes.sc old mode 100644 new mode 100755 index ebf46cb..0825eff --- a/PHP/30_anonymous_classes/1_instance_30_anonymous_classes/1_instance_30_anonymous_classes.sc +++ b/PHP/30_anonymous_classes/1_instance_30_anonymous_classes/1_instance_30_anonymous_classes.sc @@ -1,6 +1,6 @@ -@main def main(name : String): Unit = { - importCpg(name) - val x30 = (name, "30_anonymous_classes_iall", cpg.call(".*DECLARE_ANON_CLASS.*").location.toJson); - println(x30) - delete; +@main def main(name : String): Unit = { + importCpg(name) + val x30 = (name, "30_anonymous_classes_iall", cpg.call(".*DECLARE_ANON_CLASS.*").location.toJson); + println(x30) + delete; } \ No newline at end of file diff --git a/PHP/30_anonymous_classes/30_anonymous_classes.json b/PHP/30_anonymous_classes/30_anonymous_classes.json old mode 100644 new mode 100755 index acd0ef6..7062bf5 --- a/PHP/30_anonymous_classes/30_anonymous_classes.json +++ b/PHP/30_anonymous_classes/30_anonymous_classes.json @@ -1,14 +1,14 @@ -{ - "name": "Anonymous Classes", - "description": "", - "family": "code_pattern_php", - "tags": [ - "sast", - "php", - "php_v7.4.9" - ], - "instances": [ - "./1_instance_30_anonymous_classes/1_instance_30_anonymous_classes.json" - ], - "version": "v0.draft" +{ + "name": "Anonymous Classes", + "description": "./docs/description.md", + "family": "code_pattern_php", + "tags": [ + "sast", + "php", + "php_v7.4.9" + ], + "instances": [ + "./1_instance_30_anonymous_classes/1_instance_30_anonymous_classes.json" + ], + "version": "v1.0" } \ No newline at end of file diff --git a/PHP/30_anonymous_classes/Pattern Anonymous Classes.md b/PHP/30_anonymous_classes/Pattern Anonymous Classes.md deleted file mode 100644 index 3dcce20..0000000 --- a/PHP/30_anonymous_classes/Pattern Anonymous Classes.md +++ /dev/null @@ -1,88 +0,0 @@ -# Pattern: Anonymous Classes - -## Category - -Objects - -## Definition - -## Instances - -### Instance 1 - -- CATEGORY: S0 -- FEATURE vs INTERNAL API: FEATURE -- INPUT SANITIZERS: NO -- SOURCES AND SINKS: NO -- NEGATIVE TEST CASES: NO -- CODE: - -```php -log($b); -``` - -- MEASUREMENT: - -| Tool | RIPS | phpSAFE | WAP | Progpilot | Comm_1 | Comm_2 | Correct | -| ------------- | ---- | ------- | ---- | --------- | ------- | --------- | ------- | -| Vulnerability |NO | NO | NO | NO | YES | YES | YES | -Measurements Date: 8 June 2021 - -- OPCODE: - -```bash -$_main: ; (lines=14, args=0, vars=2, tmps=8) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/68_anonymous_classes/68_anonymous_classes.php:1-10 -L0 (2): EXT_STMT -L1 (2): T2 = FETCH_R (global) string("_GET") -L2 (2): T3 = FETCH_DIM_R T2 string("p1") -L3 (2): ASSIGN CV0($b) T3 -L4 (3): EXT_STMT -L5 (3): V5 = DECLARE_ANON_CLASS string("class@anonymous") -L6 (3): V6 = NEW 0 V5 -L7 (3): DO_FCALL -L8 (3): ASSIGN CV1($util) V6 -L9 (10): EXT_STMT -L10 (10): INIT_METHOD_CALL 1 CV1($util) string("log") -L11 (10): SEND_VAR_EX CV0($b) 1 -L12 (10): DO_FCALL -L13 (10): RETURN int(1) -LIVE RANGES: - 6: L7 - L8 (new) - -class@anonymous::log: ; (lines=6, args=1, vars=1, tmps=0) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/68_anonymous_classes/68_anonymous_classes.php:4-7 -L0 (4): EXT_NOP -L1 (4): CV0($msg) = RECV 1 -L2 (6): EXT_STMT -L3 (6): ECHO CV0($msg) -L4 (7): EXT_STMT -L5 (7): RETURN null -``` - -- DISCOVERY: - -```bash -cpg.call(".*DECLARE_ANON_CLASS.*").location.l -``` - -- PRECONDITIONS: - 1. - -- TRANSFORMATION: - -``` - -``` - diff --git a/PHP/30_anonymous_classes/README.md b/PHP/30_anonymous_classes/README.md new file mode 100755 index 0000000..0cce90a --- /dev/null +++ b/PHP/30_anonymous_classes/README.md @@ -0,0 +1,129 @@ +[//]: # (This file is automatically generated. If you wish to make any changes, please use the JSON files and regenerate this file using the tpframework.) + +# Anonymous Classes + +Tags: sast, php, php_v7.4.9 + +Version: v1.0 + +## Description + +Anonymous classes are classes without a specific name. They can be useful for one-off objects. + +## Overview + +| Instances | has discovery rule | discovery method | rule successfull | +|---------------------------|----------------------|--------------------|--------------------| +| [1 Instance](#1-instance) | yes | joern | yes | + +## 1 Instance + +This instance demonstrates the use of anonymous classes. + +### Code + +```PHP +log($b); +echo $a; // sink +``` + +### Instance Properties + +| category | feature_vs_internal_api | input_sanitizer | negative_test_case | source_and_sink | +|------------|---------------------------|-------------------|----------------------|-------------------| +| S0 | FEATURE | no | no | no | + +
+ +More + +
+ + +### Compile + + +```bash +$_main: + ; (lines=13, args=0, vars=3, tmps=9) + ; (before optimizer) + ; /.../PHP/30_anonymous_classes/1_instance_30_anonymous_classes/1_instance_30_anonymous_classes.php:1-10 + ; return [] RANGE[0..0] +0000 T3 = FETCH_R (global) string("_GET") +0001 T4 = FETCH_DIM_R T3 string("p1") +0002 ASSIGN CV0($b) T4 +0003 V6 = DECLARE_ANON_CLASS string("class@anonymous") +0004 V7 = NEW 0 V6 +0005 DO_FCALL +0006 ASSIGN CV1($util) V7 +0007 INIT_METHOD_CALL 1 CV1($util) string("log") +0008 SEND_VAR_EX CV0($b) 1 +0009 V10 = DO_FCALL +0010 ASSIGN CV2($a) V10 +0011 ECHO CV2($a) +0012 RETURN int(1) +LIVE RANGES: + 7: 0005 - 0006 (new) + +class@anonymous::log: + ; (lines=3, args=1, vars=1, tmps=0) + ; (before optimizer) + ; /.../PHP/30_anonymous_classes/1_instance_30_anonymous_classes/1_instance_30_anonymous_classes.php:4-6 + ; return [] RANGE[0..0] +0000 CV0($msg) = RECV 1 +0001 RETURN CV0($msg) +0002 RETURN null +``` + +
+ +
+ + +### Discovery + + +The opecode searches for `DECLARE_ANON_CLASS` in the opcode. + +```scala +val x30 = (name, "30_anonymous_classes_iall", cpg.call(".*DECLARE_ANON_CLASS.*").location.toJson); +``` + +| discovery method | expected accuracy | +|--------------------|---------------------| +| joern | Perfect | + +
+ +
+ + +### Measurement + + +| Tool | Comm_1 | Comm_2 | phpSAFE | Progpilot | RIPS | WAP | Ground Truth | +|-------------|----------|----------|-----------|-------------|--------|-------|----------------| +| 08 Jun 2021 | yes | yes | no | no | no | no | yes | +| 17 May 2023 | yes | yes | | | | | yes | + +
+ +
+ + +### Remediation + + +If named classes are easier for SAST tools, it should be possible to transform an anonymous class into a named class. + +
+ +
diff --git a/PHP/30_anonymous_classes/docs/description.md b/PHP/30_anonymous_classes/docs/description.md new file mode 100755 index 0000000..0d5f24d --- /dev/null +++ b/PHP/30_anonymous_classes/docs/description.md @@ -0,0 +1 @@ +Anonymous classes are classes without a specific name. They can be useful for one-off objects. \ No newline at end of file diff --git a/PHP/31_static_method_variable/1_instance_31_static_method_variable/1_instance_31_static_method_variable.bash b/PHP/31_static_method_variable/1_instance_31_static_method_variable/1_instance_31_static_method_variable.bash new file mode 100755 index 0000000..4d6f43c --- /dev/null +++ b/PHP/31_static_method_variable/1_instance_31_static_method_variable/1_instance_31_static_method_variable.bash @@ -0,0 +1,25 @@ + +$_main: + ; (lines=10, args=0, vars=3, tmps=6) + ; (before optimizer) + ; /.../PHP/31_static_method_variable/1_instance_31_static_method_variable/1_instance_31_static_method_variable.php:1-12 + ; return [] RANGE[0..0] +0000 T3 = FETCH_R (global) string("_GET") +0001 T4 = FETCH_DIM_R T3 string("p1") +0002 ASSIGN CV0($a) T4 +0003 ASSIGN CV1($func) string("F") +0004 INIT_STATIC_METHOD_CALL 1 string("myclass") CV1($func) +0005 SEND_VAR_EX CV0($a) 1 +0006 V7 = DO_FCALL +0007 ASSIGN CV2($b) V7 +0008 ECHO CV2($b) +0009 RETURN int(1) + +myclass::F: + ; (lines=3, args=1, vars=1, tmps=0) + ; (before optimizer) + ; /.../PHP/31_static_method_variable/1_instance_31_static_method_variable/1_instance_31_static_method_variable.php:3-5 + ; return [] RANGE[0..0] +0000 CV0($b) = RECV 1 +0001 RETURN CV0($b) +0002 RETURN null diff --git a/PHP/31_static_method_variable/1_instance_31_static_method_variable/1_instance_31_static_method_variable.json b/PHP/31_static_method_variable/1_instance_31_static_method_variable/1_instance_31_static_method_variable.json old mode 100644 new mode 100755 index af7b917..187bb2a --- a/PHP/31_static_method_variable/1_instance_31_static_method_variable/1_instance_31_static_method_variable.json +++ b/PHP/31_static_method_variable/1_instance_31_static_method_variable/1_instance_31_static_method_variable.json @@ -1,37 +1,38 @@ -{ - "code": { - "path": "./1_instance_31_static_method_variable.php", - "injection_skeleton_broken": false - }, - "discovery": { - "rule": "./1_instance_31_static_method_variable.sc", - "method": "joern", - "rule_accuracy": null, - "notes": null - }, - "compile": { - "binary": "./1_instance_31_static_method_variable.bash", - "instruction": null, - "dependencies": null - }, - "expectation": { - "type": "xss", - "sink_file": "./1_instance_31_static_method_variable.php", - "sink_line": 6, - "source_file": "./1_instance_31_static_method_variable.php", - "source_line": 10, - "expectation": true - }, - "properties": { - "category": "D2", - "feature_vs_internal_api": "FEATURE", - "input_sanitizer": false, - "source_and_sink": false, - "negative_test_case": false - }, - "remediation": { - "notes": "", - "transformation": null, - "modeling_rule": null - } +{ + "description": "This instance shows the use of a static function.", + "code": { + "path": "./1_instance_31_static_method_variable.php", + "injection_skeleton_broken": false + }, + "discovery": { + "rule": "./1_instance_31_static_method_variable.sc", + "method": "joern", + "rule_accuracy": "FP", + "notes": "The rule searches for `INIT_STATIC_METHOD_CALL` in opcode, where one argument is a variable." + }, + "compile": { + "binary": "./1_instance_31_static_method_variable.bash", + "instruction": null, + "dependencies": null + }, + "expectation": { + "type": "xss", + "sink_file": "./1_instance_31_static_method_variable.php", + "sink_line": 11, + "source_file": "./1_instance_31_static_method_variable.php", + "source_line": 8, + "expectation": true + }, + "properties": { + "category": "D2", + "feature_vs_internal_api": "FEATURE", + "input_sanitizer": false, + "source_and_sink": false, + "negative_test_case": false + }, + "remediation": { + "notes": "", + "transformation": null, + "modeling_rule": null + } } \ No newline at end of file diff --git a/PHP/31_static_method_variable/1_instance_31_static_method_variable/1_instance_31_static_method_variable.php b/PHP/31_static_method_variable/1_instance_31_static_method_variable/1_instance_31_static_method_variable.php old mode 100644 new mode 100755 index 5398b28..e9d321d --- a/PHP/31_static_method_variable/1_instance_31_static_method_variable/1_instance_31_static_method_variable.php +++ b/PHP/31_static_method_variable/1_instance_31_static_method_variable/1_instance_31_static_method_variable.php @@ -1,13 +1,11 @@ - + +More + +
+ + +### Compile + + +```bash +$_main: + ; (lines=10, args=0, vars=3, tmps=6) + ; (before optimizer) + ; /.../PHP/31_static_method_variable/1_instance_31_static_method_variable/1_instance_31_static_method_variable.php:1-12 + ; return [] RANGE[0..0] +0000 T3 = FETCH_R (global) string("_GET") +0001 T4 = FETCH_DIM_R T3 string("p1") +0002 ASSIGN CV0($a) T4 +0003 ASSIGN CV1($func) string("F") +0004 INIT_STATIC_METHOD_CALL 1 string("myclass") CV1($func) +0005 SEND_VAR_EX CV0($a) 1 +0006 V7 = DO_FCALL +0007 ASSIGN CV2($b) V7 +0008 ECHO CV2($b) +0009 RETURN int(1) + +myclass::F: + ; (lines=3, args=1, vars=1, tmps=0) + ; (before optimizer) + ; /.../PHP/31_static_method_variable/1_instance_31_static_method_variable/1_instance_31_static_method_variable.php:3-5 + ; return [] RANGE[0..0] +0000 CV0($b) = RECV 1 +0001 RETURN CV0($b) +0002 RETURN null +``` + +
+ +
+ + +### Discovery + + +The rule searches for `INIT_STATIC_METHOD_CALL` in opcode, where one argument is a variable. + +```scala +val x31 = (name, "31_static_method_variable_iall", cpg.call(".*INIT_STATIC_METHOD_CALL.*").argument.order(2).code("CV.*|T.*|V.*").astParent.location.toJson); +``` + +| discovery method | expected accuracy | +|--------------------|---------------------| +| joern | FP | + +
+ +
+ + +### Measurement + + +| Tool | Comm_1 | Comm_2 | phpSAFE | Progpilot | RIPS | WAP | Ground Truth | +|-------------|----------|----------|-----------|-------------|--------|-------|----------------| +| 08 Jun 2021 | no | no | no | no | no | no | yes | +| 17 May 2023 | no | yes | | | | | yes | + +
+ + diff --git a/PHP/31_static_method_variable/docs/description.md b/PHP/31_static_method_variable/docs/description.md new file mode 100755 index 0000000..39ab77f --- /dev/null +++ b/PHP/31_static_method_variable/docs/description.md @@ -0,0 +1 @@ +In PHP a static method can be stored in a variable and than be called from that variable. \ No newline at end of file diff --git a/PHP/32_set_overloading/1_instance_32_set_overloading/1_instance_32_set_overloading.bash b/PHP/32_set_overloading/1_instance_32_set_overloading/1_instance_32_set_overloading.bash old mode 100644 new mode 100755 index 9b6d183..8cc2a74 --- a/PHP/32_set_overloading/1_instance_32_set_overloading/1_instance_32_set_overloading.bash +++ b/PHP/32_set_overloading/1_instance_32_set_overloading/1_instance_32_set_overloading.bash @@ -1,30 +1,27 @@ - -$_main: ; (lines=13, args=0, vars=2, tmps=7) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/29_set_overloading/29_set_overloading.php:1-12 -L0 (3): NOP -L1 (10): EXT_STMT -L2 (10): T2 = FETCH_R (global) string("_GET") -L3 (10): T3 = FETCH_DIM_R T2 string("p1") -L4 (10): ASSIGN CV0($b) T3 -L5 (11): EXT_STMT -L6 (11): V5 = NEW 0 string("PropertyTest") -L7 (11): DO_FCALL -L8 (11): ASSIGN CV1($obj) V5 -L9 (12): EXT_STMT -L10 (12): ASSIGN_OBJ CV1($obj) string("var") -L11 (12): OP_DATA CV0($b) -L12 (12): RETURN int(1) -LIVE RANGES: - 5: L7 - L8 (new) - -PropertyTest::__set: ; (lines=7, args=2, vars=2, tmps=0) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/29_set_overloading/29_set_overloading.php:5-7 -L0 (5): EXT_NOP -L1 (5): CV0($name) = RECV 1 -L2 (5): CV1($value) = RECV 2 -L3 (6): EXT_STMT -L4 (6): ECHO CV1($value) -L5 (7): EXT_STMT -L6 (7): RETURN null + +$_main: + ; (lines=9, args=0, vars=2, tmps=7) + ; (before optimizer) + ; /.../PHP/32_set_overloading/1_instance_32_set_overloading/1_instance_32_set_overloading.php:1-12 + ; return [] RANGE[0..0] +0000 T2 = FETCH_R (global) string("_GET") +0001 T3 = FETCH_DIM_R T2 string("p1") +0002 ASSIGN CV0($b) T3 +0003 V5 = NEW 0 string("PropertyTest") +0004 DO_FCALL +0005 ASSIGN CV1($obj) V5 +0006 ASSIGN_OBJ CV1($obj) string("var") +0007 OP_DATA CV0($b) +0008 RETURN int(1) +LIVE RANGES: + 5: 0004 - 0005 (new) + +PropertyTest::__set: + ; (lines=4, args=2, vars=2, tmps=0) + ; (before optimizer) + ; /.../PHP/32_set_overloading/1_instance_32_set_overloading/1_instance_32_set_overloading.php:5-7 + ; return [] RANGE[0..0] +0000 CV0($name) = RECV 1 +0001 CV1($value) = RECV 2 +0002 ECHO CV1($value) +0003 RETURN null diff --git a/PHP/32_set_overloading/1_instance_32_set_overloading/1_instance_32_set_overloading.json b/PHP/32_set_overloading/1_instance_32_set_overloading/1_instance_32_set_overloading.json old mode 100644 new mode 100755 index a0af71b..3dc93ca --- a/PHP/32_set_overloading/1_instance_32_set_overloading/1_instance_32_set_overloading.json +++ b/PHP/32_set_overloading/1_instance_32_set_overloading/1_instance_32_set_overloading.json @@ -1,37 +1,38 @@ -{ - "code": { - "path": "./1_instance_32_set_overloading.php", - "injection_skeleton_broken": false - }, - "discovery": { - "rule": "./1_instance_32_set_overloading.sc", - "method": "joern", - "rule_accuracy": null, - "notes": null - }, - "compile": { - "binary": "./1_instance_32_set_overloading.bash", - "instruction": null, - "dependencies": null - }, - "expectation": { - "type": "xss", - "sink_file": "./1_instance_32_set_overloading.php", - "sink_line": 6, - "source_file": "./1_instance_32_set_overloading.php", - "source_line": 10, - "expectation": true - }, - "properties": { - "category": "S0", - "feature_vs_internal_api": "FEATURE", - "input_sanitizer": false, - "source_and_sink": false, - "negative_test_case": false - }, - "remediation": { - "notes": "", - "transformation": null, - "modeling_rule": null - } +{ + "description": "The instance shows, that writing data to the existing but private variable `var` of `obj` triggers `__set` to be called, which is vulnerable.", + "code": { + "path": "./1_instance_32_set_overloading.php", + "injection_skeleton_broken": true + }, + "discovery": { + "rule": "../32_set_overloading.sc", + "method": "joern", + "rule_accuracy": "FP", + "notes": "../docs/discovery_notes.md" + }, + "compile": { + "binary": "./1_instance_32_set_overloading.bash", + "instruction": null, + "dependencies": null + }, + "expectation": { + "type": "xss", + "sink_file": "./1_instance_32_set_overloading.php", + "sink_line": 6, + "source_file": "./1_instance_32_set_overloading.php", + "source_line": 10, + "expectation": true + }, + "properties": { + "category": "S0", + "feature_vs_internal_api": "FEATURE", + "input_sanitizer": false, + "source_and_sink": false, + "negative_test_case": false + }, + "remediation": { + "notes": "", + "transformation": null, + "modeling_rule": null + } } \ No newline at end of file diff --git a/PHP/32_set_overloading/1_instance_32_set_overloading/1_instance_32_set_overloading.php b/PHP/32_set_overloading/1_instance_32_set_overloading/1_instance_32_set_overloading.php old mode 100644 new mode 100755 index d1b68c9..902e665 --- a/PHP/32_set_overloading/1_instance_32_set_overloading/1_instance_32_set_overloading.php +++ b/PHP/32_set_overloading/1_instance_32_set_overloading/1_instance_32_set_overloading.php @@ -1,12 +1,12 @@ -var = $b; \ No newline at end of file diff --git a/PHP/32_set_overloading/1_instance_32_set_overloading/1_instance_32_set_overloading.sc b/PHP/32_set_overloading/1_instance_32_set_overloading/1_instance_32_set_overloading.sc deleted file mode 100644 index b0c7a1c..0000000 --- a/PHP/32_set_overloading/1_instance_32_set_overloading/1_instance_32_set_overloading.sc +++ /dev/null @@ -1,7 +0,0 @@ -@main def main(name : String): Unit = { - importCpg(name) - def methods1 = cpg.method.name("__set").name.l - val x32 = (name, "32_set_overloading_iall", cpg.call("NEW").argument.filter{x => methods1.contains(x.code.toLowerCase)}.location.toJson); - println(x32) - delete; -} \ No newline at end of file diff --git a/PHP/32_set_overloading/2_instance_32_set_overloading/2_instance_32_set_overloading.bash b/PHP/32_set_overloading/2_instance_32_set_overloading/2_instance_32_set_overloading.bash new file mode 100755 index 0000000..621f809 --- /dev/null +++ b/PHP/32_set_overloading/2_instance_32_set_overloading/2_instance_32_set_overloading.bash @@ -0,0 +1,27 @@ + +$_main: + ; (lines=9, args=0, vars=2, tmps=7) + ; (before optimizer) + ; /.../PHP/32_set_overloading/2_instance_32_set_overloading/2_instance_32_set_overloading.php:1-12 + ; return [] RANGE[0..0] +0000 T2 = FETCH_R (global) string("_GET") +0001 T3 = FETCH_DIM_R T2 string("p1") +0002 ASSIGN CV0($b) T3 +0003 V5 = NEW 0 string("PropertyTest") +0004 DO_FCALL +0005 ASSIGN CV1($obj) V5 +0006 ASSIGN_OBJ CV1($obj) string("x") +0007 OP_DATA CV0($b) +0008 RETURN int(1) +LIVE RANGES: + 5: 0004 - 0005 (new) + +PropertyTest::__set: + ; (lines=4, args=2, vars=2, tmps=0) + ; (before optimizer) + ; /.../PHP/32_set_overloading/2_instance_32_set_overloading/2_instance_32_set_overloading.php:5-7 + ; return [] RANGE[0..0] +0000 CV0($name) = RECV 1 +0001 CV1($value) = RECV 2 +0002 ECHO CV1($value) +0003 RETURN null diff --git a/PHP/32_set_overloading/2_instance_32_set_overloading/2_instance_32_set_overloading.json b/PHP/32_set_overloading/2_instance_32_set_overloading/2_instance_32_set_overloading.json new file mode 100755 index 0000000..e161b8d --- /dev/null +++ b/PHP/32_set_overloading/2_instance_32_set_overloading/2_instance_32_set_overloading.json @@ -0,0 +1,38 @@ +{ + "description": "This instance shows, that writing data to the non-existing variable `x` of the object `obj` will invoke the `__set` function.", + "code": { + "path": "./2_instance_32_set_overloading.php", + "injection_skeleton_broken": true + }, + "expectation": { + "type": "xss", + "sink_file": "./2_instance_32_set_overloading.php", + "sink_line": 6, + "source_file": "./2_instance_32_set_overloading.php", + "source_line": 10, + "expectation": true + }, + "compile": { + "binary": "./2_instance_32_set_overloading.bash", + "instruction": null, + "dependencies": null + }, + "discovery": { + "rule": "../32_set_overloading.sc", + "method": "joern", + "rule_accuracy": "FP", + "notes": "../docs/discovery_notes.md" + }, + "properties": { + "category": "S0", + "feature_vs_internal_api": "FEATURE", + "input_sanitizer": false, + "source_and_sink": false, + "negative_test_case": false + }, + "remediation": { + "notes": "", + "transformation": null, + "modeling_rule": null + } +} \ No newline at end of file diff --git a/PHP/32_set_overloading/2_instance_32_set_overloading/2_instance_32_set_overloading.php b/PHP/32_set_overloading/2_instance_32_set_overloading/2_instance_32_set_overloading.php new file mode 100755 index 0000000..c0c01c8 --- /dev/null +++ b/PHP/32_set_overloading/2_instance_32_set_overloading/2_instance_32_set_overloading.php @@ -0,0 +1,12 @@ +x = $b; \ No newline at end of file diff --git a/PHP/32_set_overloading/32_set_overloading.json b/PHP/32_set_overloading/32_set_overloading.json old mode 100644 new mode 100755 index f81ee57..40b6142 --- a/PHP/32_set_overloading/32_set_overloading.json +++ b/PHP/32_set_overloading/32_set_overloading.json @@ -1,14 +1,15 @@ -{ - "name": "Set Overloading", - "description": "", - "family": "code_pattern_php", - "tags": [ - "sast", - "php", - "php_v7.4.9" - ], - "instances": [ - "./1_instance_32_set_overloading/1_instance_32_set_overloading.json" - ], - "version": "v0.draft" +{ + "name": "Set Overloading", + "description": "./docs/description.md", + "family": "code_pattern_php", + "tags": [ + "sast", + "php", + "php_v7.4.9" + ], + "instances": [ + "./1_instance_32_set_overloading/1_instance_32_set_overloading.json", + "./2_instance_32_set_overloading/2_instance_32_set_overloading.json" + ], + "version": "v1.0" } \ No newline at end of file diff --git a/PHP/32_set_overloading/32_set_overloading.sc b/PHP/32_set_overloading/32_set_overloading.sc new file mode 100755 index 0000000..29fc928 --- /dev/null +++ b/PHP/32_set_overloading/32_set_overloading.sc @@ -0,0 +1,7 @@ +@main def main(name : String): Unit = { + importCpg(name) + def methodClasses = cpg.method.name("__set").astParentFullName.l + val x32 = (name, "32_set_overloading_iall", cpg.call("NEW").argument.filter{x => methodClasses.contains(x.code.toLowerCase)}.location.toJson); + println(x32) + delete; +} \ No newline at end of file diff --git a/PHP/32_set_overloading/Pattern Set Overloading.md b/PHP/32_set_overloading/Pattern Set Overloading.md deleted file mode 100644 index 307fb27..0000000 --- a/PHP/32_set_overloading/Pattern Set Overloading.md +++ /dev/null @@ -1,94 +0,0 @@ -# Pattern: Set Overloading - -## Category - -Objects - -## Definition - -## Instances - -### Instance 1 - -- CATEGORY: S0 -- FEATURE vs INTERNAL API: FEATURE -- INPUT SANITIZERS: NO -- SOURCES AND SINKS: NO -- NEGATIVE TEST CASES: NO -- CODE: - -```php -var = $b; -``` - -- MEASUREMENT: - -| Tool | RIPS | phpSAFE | WAP | Progpilot | Comm_1 | Comm_2 | Correct | -| ------------- | ---- | ------- | ---- | --------- | ------- | --------- | ------- | -| Vulnerability | NO | NO | NO | NO | YES | NO | YES | -Measurements Date: 8 June 2021 - -- OPCODE: - -```bash -$_main: ; (lines=13, args=0, vars=2, tmps=7) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/29_set_overloading/29_set_overloading.php:1-12 -L0 (3): NOP -L1 (10): EXT_STMT -L2 (10): T2 = FETCH_R (global) string("_GET") -L3 (10): T3 = FETCH_DIM_R T2 string("p1") -L4 (10): ASSIGN CV0($b) T3 -L5 (11): EXT_STMT -L6 (11): V5 = NEW 0 string("PropertyTest") -L7 (11): DO_FCALL -L8 (11): ASSIGN CV1($obj) V5 -L9 (12): EXT_STMT -L10 (12): ASSIGN_OBJ CV1($obj) string("var") -L11 (12): OP_DATA CV0($b) -L12 (12): RETURN int(1) -LIVE RANGES: - 5: L7 - L8 (new) - -PropertyTest::__set: ; (lines=7, args=2, vars=2, tmps=0) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/29_set_overloading/29_set_overloading.php:5-7 -L0 (5): EXT_NOP -L1 (5): CV0($name) = RECV 1 -L2 (5): CV1($value) = RECV 2 -L3 (6): EXT_STMT -L4 (6): ECHO CV1($value) -L5 (7): EXT_STMT -L6 (7): RETURN null -``` - -- DISCOVERY: - -```bash -def methods1 = cpg.typeDecl.filter{x => x.method.name.l.contains("__set")}.name.l -cpg.call("NEW").argument.filter{x => methods1.contains(x.code.toLowerCase)}.size; -``` - -- PRECONDITIONS: - 1. - -- TRANSFORMATION: T1 - -Call the function directly. - -``` - -``` - - diff --git a/PHP/32_set_overloading/README.md b/PHP/32_set_overloading/README.md new file mode 100755 index 0000000..716f4b0 --- /dev/null +++ b/PHP/32_set_overloading/README.md @@ -0,0 +1,237 @@ +[//]: # (This file is automatically generated. If you wish to make any changes, please use the JSON files and regenerate this file using the tpframework.) + +# Set Overloading + +Tags: sast, php, php_v7.4.9 + +Version: v1.0 + +## Description + +In many object-oriented programming languages overloading provides the ability of having multiple methods with the same name but different quantities and types of arguments. In PHP however [Overloading](https://www.php.net/manual/en/language.oop5.overloading.php) describes the concept of dynamically creating properties and methods. This pattern targets the usage of property overloading for `__set()`. This method is run when data is written to inaccessible (protected or private) or non-existing properties. + +## Overview + +| Instances | has discovery rule | discovery method | rule successfull | +|---------------------------|----------------------|--------------------|--------------------| +| [1 Instance](#1-instance) | yes | joern | yes | +| [2 Instance](#2-instance) | yes | joern | yes | + +
+ + +## 1 Instance + + +The instance shows, that writing data to the existing but private variable `var` of `obj` triggers `__set` to be called, which is vulnerable. + +### Code + +```PHP +var = $b; +``` + +### Instance Properties + +| category | feature_vs_internal_api | input_sanitizer | negative_test_case | source_and_sink | +|------------|---------------------------|-------------------|----------------------|-------------------| +| S0 | FEATURE | no | no | no | + +
+ +More + +
+ + +### Compile + + +```bash +$_main: + ; (lines=9, args=0, vars=2, tmps=7) + ; (before optimizer) + ; /.../PHP/32_set_overloading/1_instance_32_set_overloading/1_instance_32_set_overloading.php:1-12 + ; return [] RANGE[0..0] +0000 T2 = FETCH_R (global) string("_GET") +0001 T3 = FETCH_DIM_R T2 string("p1") +0002 ASSIGN CV0($b) T3 +0003 V5 = NEW 0 string("PropertyTest") +0004 DO_FCALL +0005 ASSIGN CV1($obj) V5 +0006 ASSIGN_OBJ CV1($obj) string("var") +0007 OP_DATA CV0($b) +0008 RETURN int(1) +LIVE RANGES: + 5: 0004 - 0005 (new) + +PropertyTest::__set: + ; (lines=4, args=2, vars=2, tmps=0) + ; (before optimizer) + ; /.../PHP/32_set_overloading/1_instance_32_set_overloading/1_instance_32_set_overloading.php:5-7 + ; return [] RANGE[0..0] +0000 CV0($name) = RECV 1 +0001 CV1($value) = RECV 2 +0002 ECHO CV1($value) +0003 RETURN null +``` + +
+ +
+ + +### Discovery + + +The discovery rule first gets all class names, where the method `__set` is defined. +Afterwards it collects all `NEW` calles and filters them to see which of the instanciated objects has the method `__set` defined. +The rule would be perfect, if we could additionally check, if one of the created objects invokes the `__set` method within its lifetime. + +```scala +def methodClasses = cpg.method.name("__set").astParentFullName.l +val x32 = (name, "32_set_overloading_iall", cpg.call("NEW").argument.filter{x => methodClasses.contains(x.code.toLowerCase)}.location.toJson); +``` + +| discovery method | expected accuracy | +|--------------------|---------------------| +| joern | FP | + +
+ +
+ + +### Measurement + + +| Tool | Comm_1 | Comm_2 | phpSAFE | Progpilot | RIPS | WAP | Ground Truth | +|-------------|----------|----------|-----------|-------------|--------|-------|----------------| +| 08 Jun 2021 | yes | no | no | no | no | no | yes | +| 17 May 2023 | yes | no | | | | | yes | + +
+ +
+ +
+ +
+ + +## 2 Instance + + +This instance shows, that writing data to the non-existing variable `x` of the object `obj` will invoke the `__set` function. + +### Code + +```PHP +x = $b; +``` + +### Instance Properties + +| category | feature_vs_internal_api | input_sanitizer | negative_test_case | source_and_sink | +|------------|---------------------------|-------------------|----------------------|-------------------| +| S0 | FEATURE | no | no | no | + +
+ +More + +
+ + +### Compile + + +```bash +$_main: + ; (lines=9, args=0, vars=2, tmps=7) + ; (before optimizer) + ; /.../PHP/32_set_overloading/2_instance_32_set_overloading/2_instance_32_set_overloading.php:1-12 + ; return [] RANGE[0..0] +0000 T2 = FETCH_R (global) string("_GET") +0001 T3 = FETCH_DIM_R T2 string("p1") +0002 ASSIGN CV0($b) T3 +0003 V5 = NEW 0 string("PropertyTest") +0004 DO_FCALL +0005 ASSIGN CV1($obj) V5 +0006 ASSIGN_OBJ CV1($obj) string("x") +0007 OP_DATA CV0($b) +0008 RETURN int(1) +LIVE RANGES: + 5: 0004 - 0005 (new) + +PropertyTest::__set: + ; (lines=4, args=2, vars=2, tmps=0) + ; (before optimizer) + ; /.../PHP/32_set_overloading/2_instance_32_set_overloading/2_instance_32_set_overloading.php:5-7 + ; return [] RANGE[0..0] +0000 CV0($name) = RECV 1 +0001 CV1($value) = RECV 2 +0002 ECHO CV1($value) +0003 RETURN null +``` + +
+ +
+ + +### Discovery + + +The discovery rule first gets all class names, where the method `__set` is defined. +Afterwards it collects all `NEW` calles and filters them to see which of the instanciated objects has the method `__set` defined. +The rule would be perfect, if we could additionally check, if one of the created objects invokes the `__set` method within its lifetime. + +```scala +def methodClasses = cpg.method.name("__set").astParentFullName.l +val x32 = (name, "32_set_overloading_iall", cpg.call("NEW").argument.filter{x => methodClasses.contains(x.code.toLowerCase)}.location.toJson); +``` + +| discovery method | expected accuracy | +|--------------------|---------------------| +| joern | FP | + +
+ +
+ + +### Measurement + + +| Tool | Comm_1 | Comm_2 | Ground Truth | +|-------------|----------|----------|----------------| +| 17 May 2023 | yes | no | yes | + +
+ +
+ +
diff --git a/PHP/32_set_overloading/docs/description.md b/PHP/32_set_overloading/docs/description.md new file mode 100755 index 0000000..d5203e0 --- /dev/null +++ b/PHP/32_set_overloading/docs/description.md @@ -0,0 +1 @@ +In many object-oriented programming languages overloading provides the ability of having multiple methods with the same name but different quantities and types of arguments. In PHP however [Overloading](https://www.php.net/manual/en/language.oop5.overloading.php) describes the concept of dynamically creating properties and methods. This pattern targets the usage of property overloading for `__set()`. This method is run when data is written to inaccessible (protected or private) or non-existing properties. \ No newline at end of file diff --git a/PHP/32_set_overloading/docs/discovery_notes.md b/PHP/32_set_overloading/docs/discovery_notes.md new file mode 100755 index 0000000..53ad332 --- /dev/null +++ b/PHP/32_set_overloading/docs/discovery_notes.md @@ -0,0 +1,3 @@ +The discovery rule first gets all class names, where the method `__set` is defined. +Afterwards it collects all `NEW` calles and filters them to see which of the instanciated objects has the method `__set` defined. +The rule would be perfect, if we could additionally check, if one of the created objects invokes the `__set` method within its lifetime. \ No newline at end of file diff --git a/PHP/33_get_overloading/1_instance_33_get_overloading/1_instance_33_get_overloading.bash b/PHP/33_get_overloading/1_instance_33_get_overloading/1_instance_33_get_overloading.bash new file mode 100755 index 0000000..75cbd55 --- /dev/null +++ b/PHP/33_get_overloading/1_instance_33_get_overloading/1_instance_33_get_overloading.bash @@ -0,0 +1,42 @@ + +$_main: + ; (lines=11, args=0, vars=2, tmps=8) + ; (before optimizer) + ; /.../PHP/33_get_overloading/1_instance_33_get_overloading/1_instance_33_get_overloading.php:1-18 + ; return [] RANGE[0..0] +0000 T2 = FETCH_R (global) string("_GET") +0001 T3 = FETCH_DIM_R T2 string("p1") +0002 ASSIGN CV0($b) T3 +0003 V5 = NEW 0 string("PropertyTest") +0004 DO_FCALL +0005 ASSIGN CV1($obj) V5 +0006 ASSIGN_OBJ CV1($obj) string("x") +0007 OP_DATA CV0($b) +0008 T9 = FETCH_OBJ_R CV1($obj) string("x") +0009 FREE T9 +0010 RETURN int(1) +LIVE RANGES: + 5: 0004 - 0005 (new) + +PropertyTest::__set: + ; (lines=5, args=2, vars=2, tmps=1) + ; (before optimizer) + ; /.../PHP/33_get_overloading/1_instance_33_get_overloading/1_instance_33_get_overloading.php:5-7 + ; return [] RANGE[0..0] +0000 CV0($name) = RECV 1 +0001 CV1($value) = RECV 2 +0002 ASSIGN_OBJ THIS CV0($name) +0003 OP_DATA CV1($value) +0004 RETURN null + +PropertyTest::__get: + ; (lines=6, args=1, vars=1, tmps=2) + ; (before optimizer) + ; /.../PHP/33_get_overloading/1_instance_33_get_overloading/1_instance_33_get_overloading.php:9-12 + ; return [] RANGE[0..0] +0000 CV0($name) = RECV 1 +0001 T1 = FETCH_OBJ_R THIS CV0($name) +0002 ECHO T1 +0003 T2 = FETCH_OBJ_R THIS CV0($name) +0004 RETURN T2 +0005 RETURN null diff --git a/PHP/33_get_overloading/1_instance_33_get_overloading/1_instance_33_get_overloading.json b/PHP/33_get_overloading/1_instance_33_get_overloading/1_instance_33_get_overloading.json old mode 100644 new mode 100755 index f291fb3..bff1dcb --- a/PHP/33_get_overloading/1_instance_33_get_overloading/1_instance_33_get_overloading.json +++ b/PHP/33_get_overloading/1_instance_33_get_overloading/1_instance_33_get_overloading.json @@ -1,37 +1,38 @@ -{ - "code": { - "path": "./1_instance_33_get_overloading.php", - "injection_skeleton_broken": false - }, - "discovery": { - "rule": "./1_instance_33_get_overloading.sc", - "method": "joern", - "rule_accuracy": "FP", - "notes": null - }, - "compile": { - "binary": "./1_instance_33_get_overloading.bash", - "instruction": null, - "dependencies": null - }, - "expectation": { - "type": "xss", - "sink_file": "./1_instance_33_get_overloading.php", - "sink_line": 10, - "source_file": "./1_instance_33_get_overloading.php", - "source_line": 15, - "expectation": true - }, - "properties": { - "category": "S0", - "feature_vs_internal_api": "FEATURE", - "input_sanitizer": false, - "source_and_sink": false, - "negative_test_case": false - }, - "remediation": { - "notes": "", - "transformation": null, - "modeling_rule": null - } +{ + "description": "The instance shows overloading of the `__get` function.", + "code": { + "path": "./1_instance_33_get_overloading.php", + "injection_skeleton_broken": true + }, + "discovery": { + "rule": "./1_instance_33_get_overloading.sc", + "method": "joern", + "rule_accuracy": "FP", + "notes": "The discovery rule first gets all class names, where the method `__get` is defined.\nAfterwards it collects all `NEW` calles and filters them to see which of the instanciated objects has the method `__get` defined.\nThe rule would be perfect, if we could additionally check, if one of the created objects invokes the `__get` method within its lifetime." + }, + "compile": { + "binary": "./1_instance_33_get_overloading.bash", + "instruction": null, + "dependencies": null + }, + "expectation": { + "type": "xss", + "sink_file": "./1_instance_33_get_overloading.php", + "sink_line": 10, + "source_file": "./1_instance_33_get_overloading.php", + "source_line": 15, + "expectation": true + }, + "properties": { + "category": "S0", + "feature_vs_internal_api": "FEATURE", + "input_sanitizer": false, + "source_and_sink": false, + "negative_test_case": false + }, + "remediation": { + "notes": "", + "transformation": null, + "modeling_rule": null + } } \ No newline at end of file diff --git a/PHP/33_get_overloading/1_instance_33_get_overloading/1_instance_33_get_overloading.php b/PHP/33_get_overloading/1_instance_33_get_overloading/1_instance_33_get_overloading.php old mode 100644 new mode 100755 index c432a39..755f832 --- a/PHP/33_get_overloading/1_instance_33_get_overloading/1_instance_33_get_overloading.php +++ b/PHP/33_get_overloading/1_instance_33_get_overloading/1_instance_33_get_overloading.php @@ -1,18 +1,18 @@ -$name = $value; - } - - public function __get($name){ - echo $this->$name; - return $this->$name; - } -} - -$b = $_GET["p1"]; -$obj = new PropertyTest; -$obj->var = $b; -$res = $obj->var; \ No newline at end of file +$name = $value; + } + + public function __get($name) { + echo $this->$name; // sink + return $this->$name; + } +} + +$b = $_GET["p1"]; // source +$obj = new PropertyTest; +$obj->x = $b; +$obj->x; \ No newline at end of file diff --git a/PHP/33_get_overloading/1_instance_33_get_overloading/1_instance_33_get_overloading.sc b/PHP/33_get_overloading/1_instance_33_get_overloading/1_instance_33_get_overloading.sc old mode 100644 new mode 100755 index de06ad3..46d12b6 --- a/PHP/33_get_overloading/1_instance_33_get_overloading/1_instance_33_get_overloading.sc +++ b/PHP/33_get_overloading/1_instance_33_get_overloading/1_instance_33_get_overloading.sc @@ -1,7 +1,7 @@ -@main def main(name : String): Unit = { - importCpg(name) - def methods2 = cpg.method.name("__get").name.l - val x33 = (name, "33_get_overloading_iall", cpg.call("NEW").argument.filter{x => methods2.contains(x.code.toLowerCase)}.location.toJson); - println(x33) - delete; +@main def main(name : String): Unit = { + importCpg(name) + def methodClasses = cpg.method.name("__get").astParentFullName.l + val x33 = (name, "33_get_overloading_iall", cpg.call("NEW").argument.filter{x => methodClasses.contains(x.code.toLowerCase)}.location.toJson); + println(x33) + delete; } \ No newline at end of file diff --git a/PHP/33_get_overloading/33_get_overloading.json b/PHP/33_get_overloading/33_get_overloading.json old mode 100644 new mode 100755 index fc5ee58..4a7b00f --- a/PHP/33_get_overloading/33_get_overloading.json +++ b/PHP/33_get_overloading/33_get_overloading.json @@ -1,14 +1,14 @@ -{ - "name": "Get Overloading", - "description": "", - "family": "code_pattern_php", - "tags": [ - "sast", - "php", - "php_v7.4.9" - ], - "instances": [ - "./1_instance_33_get_overloading/1_instance_33_get_overloading.json" - ], - "version": "v1.0" +{ + "name": "Get Overloading", + "description": "./docs/description.md", + "family": "code_pattern_php", + "tags": [ + "sast", + "php", + "php_v7.4.9" + ], + "instances": [ + "./1_instance_33_get_overloading/1_instance_33_get_overloading.json" + ], + "version": "v1.0" } \ No newline at end of file diff --git a/PHP/33_get_overloading/Pattern Get Overloading.md b/PHP/33_get_overloading/Pattern Get Overloading.md deleted file mode 100644 index 97968b1..0000000 --- a/PHP/33_get_overloading/Pattern Get Overloading.md +++ /dev/null @@ -1,122 +0,0 @@ -# Pattern: Get Overloading - -## Category - -Objects - -## Definition - -## Instances - -### Instance 1 - -- CATEGORY: S0 -- FEATURE vs INTERNAL API: FEATURE -- INPUT SANITIZERS: NO -- SOURCES AND SINKS: NO -- NEGATIVE TEST CASES: NO -- CODE: - -```php -$name = $value; - } - - public function __get($name){ - echo $this->$name; - return $this->$name; - } -} - -$b = $_GET["p1"]; -$obj = new PropertyTest; -$obj->var = $b; -$res = $obj->var . "\n\n"; -``` - -- MEASUREMENT: - -| Tool | RIPS | phpSAFE | WAP | Progpilot | Comm_1 | Comm_2 | Correct | -| ------------- | ---- | ------- | ---- | --------- | ------- | --------- | ------- | -| Vulnerability | NO | NO | NO | NO | NO | NO | YES | -Measurements Date: 8 June 2021 - -- OPCODE: - -```bash -$_main: ; (lines=17, args=0, vars=3, tmps=10) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/30_get_overloading/30_get_overloading.php:1-18 -L0 (3): NOP -L1 (15): EXT_STMT -L2 (15): T3 = FETCH_R (global) string("_GET") -L3 (15): T4 = FETCH_DIM_R T3 string("p1") -L4 (15): ASSIGN CV0($b) T4 -L5 (16): EXT_STMT -L6 (16): V6 = NEW 0 string("PropertyTest") -L7 (16): DO_FCALL -L8 (16): ASSIGN CV1($obj) V6 -L9 (17): EXT_STMT -L10 (17): ASSIGN_OBJ CV1($obj) string("var") -L11 (17): OP_DATA CV0($b) -L12 (18): EXT_STMT -L13 (18): T10 = FETCH_OBJ_R CV1($obj) string("var") -L14 (18): T11 = CONCAT T10 string(" - -") -L15 (18): ASSIGN CV2($res) T11 -L16 (18): RETURN int(1) -LIVE RANGES: - 6: L7 - L8 (new) - -PropertyTest::__set: ; (lines=8, args=2, vars=2, tmps=1) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/30_get_overloading/30_get_overloading.php:5-7 -L0 (5): EXT_NOP -L1 (5): CV0($name) = RECV 1 -L2 (5): CV1($value) = RECV 2 -L3 (6): EXT_STMT -L4 (6): ASSIGN_OBJ THIS CV0($name) -L5 (6): OP_DATA CV1($value) -L6 (7): EXT_STMT -L7 (7): RETURN null - -PropertyTest::__get: ; (lines=10, args=1, vars=1, tmps=2) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/30_get_overloading/30_get_overloading.php:9-12 -L0 (9): EXT_NOP -L1 (9): CV0($name) = RECV 1 -L2 (10): EXT_STMT -L3 (10): T1 = FETCH_OBJ_R THIS CV0($name) -L4 (10): ECHO T1 -L5 (11): EXT_STMT -L6 (11): T2 = FETCH_OBJ_R THIS CV0($name) -L7 (11): RETURN T2 -L8 (12): EXT_STMT -L9 (12): RETURN null -``` - -- DISCOVERY: - -```bash -def methods1 = cpg.typeDecl.filter{x => x.method.name.l.contains("__get")}.name.l -cpg.call("NEW").argument.filter{x => methods1.contains(x.code.toLowerCase)}.size; -``` - -- PRECONDITIONS: - 1. - -- TRANSFORMATION: - -Call the function directly. - -``` - -``` - - - diff --git a/PHP/33_get_overloading/README.md b/PHP/33_get_overloading/README.md new file mode 100755 index 0000000..e9a8261 --- /dev/null +++ b/PHP/33_get_overloading/README.md @@ -0,0 +1,142 @@ +[//]: # (This file is automatically generated. If you wish to make any changes, please use the JSON files and regenerate this file using the tpframework.) + +# Get Overloading + +Tags: sast, php, php_v7.4.9 + +Version: v1.0 + +## Description + +In many object-oriented programming languages overloading provides the ability of having multiple methods with the same name but different quantities and types of arguments. In PHP however [Overloading](https://www.php.net/manual/en/language.oop5.overloading.php) describes the concept of dynamically creating properties and methods. This pattern targets the usage of property overloading for `__get()`. This method is run when data from inaccessible (protected or private) or non-existing properties is read. + +## Overview + +| Instances | has discovery rule | discovery method | rule successfull | +|---------------------------|----------------------|--------------------|--------------------| +| [1 Instance](#1-instance) | yes | joern | yes | + +## 1 Instance + +The instance shows overloading of the `__get` function. + +### Code + +```PHP +$name = $value; + } + + public function __get($name) { + echo $this->$name; // sink + return $this->$name; + } +} + +$b = $_GET["p1"]; // source +$obj = new PropertyTest; +$obj->x = $b; +$obj->x; +``` + +### Instance Properties + +| category | feature_vs_internal_api | input_sanitizer | negative_test_case | source_and_sink | +|------------|---------------------------|-------------------|----------------------|-------------------| +| S0 | FEATURE | no | no | no | + +
+ +More + +
+ + +### Compile + + +```bash +$_main: + ; (lines=11, args=0, vars=2, tmps=8) + ; (before optimizer) + ; /.../PHP/33_get_overloading/1_instance_33_get_overloading/1_instance_33_get_overloading.php:1-18 + ; return [] RANGE[0..0] +0000 T2 = FETCH_R (global) string("_GET") +0001 T3 = FETCH_DIM_R T2 string("p1") +0002 ASSIGN CV0($b) T3 +0003 V5 = NEW 0 string("PropertyTest") +0004 DO_FCALL +0005 ASSIGN CV1($obj) V5 +0006 ASSIGN_OBJ CV1($obj) string("x") +0007 OP_DATA CV0($b) +0008 T9 = FETCH_OBJ_R CV1($obj) string("x") +0009 FREE T9 +0010 RETURN int(1) +LIVE RANGES: + 5: 0004 - 0005 (new) + +PropertyTest::__set: + ; (lines=5, args=2, vars=2, tmps=1) + ; (before optimizer) + ; /.../PHP/33_get_overloading/1_instance_33_get_overloading/1_instance_33_get_overloading.php:5-7 + ; return [] RANGE[0..0] +0000 CV0($name) = RECV 1 +0001 CV1($value) = RECV 2 +0002 ASSIGN_OBJ THIS CV0($name) +0003 OP_DATA CV1($value) +0004 RETURN null + +PropertyTest::__get: + ; (lines=6, args=1, vars=1, tmps=2) + ; (before optimizer) + ; /.../PHP/33_get_overloading/1_instance_33_get_overloading/1_instance_33_get_overloading.php:9-12 + ; return [] RANGE[0..0] +0000 CV0($name) = RECV 1 +0001 T1 = FETCH_OBJ_R THIS CV0($name) +0002 ECHO T1 +0003 T2 = FETCH_OBJ_R THIS CV0($name) +0004 RETURN T2 +0005 RETURN null +``` + +
+ +
+ + +### Discovery + + +The discovery rule first gets all class names, where the method `__get` is defined. +Afterwards it collects all `NEW` calles and filters them to see which of the instanciated objects has the method `__get` defined. +The rule would be perfect, if we could additionally check, if one of the created objects invokes the `__get` method within its lifetime. + +```scala +def methodClasses = cpg.method.name("__get").astParentFullName.l +val x33 = (name, "33_get_overloading_iall", cpg.call("NEW").argument.filter{x => methodClasses.contains(x.code.toLowerCase)}.location.toJson); +``` + +| discovery method | expected accuracy | +|--------------------|---------------------| +| joern | FP | + +
+ +
+ + +### Measurement + + +| Tool | Comm_1 | Comm_2 | phpSAFE | Progpilot | RIPS | WAP | Ground Truth | +|-------------|----------|----------|-----------|-------------|--------|-------|----------------| +| 08 Jun 2021 | no | no | no | no | no | no | yes | +| 17 May 2023 | no | no | | | | | yes | + +
+ +
diff --git a/PHP/33_get_overloading/docs/description.md b/PHP/33_get_overloading/docs/description.md new file mode 100755 index 0000000..8a5a729 --- /dev/null +++ b/PHP/33_get_overloading/docs/description.md @@ -0,0 +1 @@ +In many object-oriented programming languages overloading provides the ability of having multiple methods with the same name but different quantities and types of arguments. In PHP however [Overloading](https://www.php.net/manual/en/language.oop5.overloading.php) describes the concept of dynamically creating properties and methods. This pattern targets the usage of property overloading for `__get()`. This method is run when data from inaccessible (protected or private) or non-existing properties is read. \ No newline at end of file diff --git a/PHP/34_isset_overloading/1_instance_34_isset_overloading/1_instance_34_isset_overloading.bash b/PHP/34_isset_overloading/1_instance_34_isset_overloading/1_instance_34_isset_overloading.bash new file mode 100755 index 0000000..2baa5e9 --- /dev/null +++ b/PHP/34_isset_overloading/1_instance_34_isset_overloading/1_instance_34_isset_overloading.bash @@ -0,0 +1,41 @@ + +$_main: + ; (lines=12, args=0, vars=2, tmps=8) + ; (before optimizer) + ; /.../PHP/34_isset_overloading/1_instance_34_isset_overloading/1_instance_34_isset_overloading.php:1-21 + ; return [] RANGE[0..0] +0000 T2 = FETCH_R (global) string("_GET") +0001 T3 = FETCH_DIM_R T2 string("p1") +0002 ASSIGN CV0($b) T3 +0003 V5 = NEW 0 string("PropertyTest") +0004 DO_FCALL +0005 ASSIGN CV1($obj) V5 +0006 INIT_METHOD_CALL 1 CV1($obj) string("setx") +0007 SEND_VAR_EX CV0($b) 1 +0008 DO_FCALL +0009 T9 = ISSET_ISEMPTY_PROP_OBJ (isset) CV1($obj) string("x") +0010 FREE T9 +0011 RETURN int(1) +LIVE RANGES: + 5: 0004 - 0005 (new) + +PropertyTest::__isset: + ; (lines=5, args=1, vars=1, tmps=1) + ; (before optimizer) + ; /.../PHP/34_isset_overloading/1_instance_34_isset_overloading/1_instance_34_isset_overloading.php:5-8 + ; return [] RANGE[0..0] +0000 CV0($name) = RECV 1 +0001 T1 = FETCH_OBJ_R THIS string("x") +0002 ECHO T1 +0003 RETURN bool(true) +0004 RETURN null + +PropertyTest::setx: + ; (lines=4, args=1, vars=1, tmps=1) + ; (before optimizer) + ; /.../PHP/34_isset_overloading/1_instance_34_isset_overloading/1_instance_34_isset_overloading.php:10-12 + ; return [] RANGE[0..0] +0000 CV0($b) = RECV 1 +0001 ASSIGN_OBJ THIS string("x") +0002 OP_DATA CV0($b) +0003 RETURN null diff --git a/PHP/34_isset_overloading/1_instance_34_isset_overloading/1_instance_34_isset_overloading.json b/PHP/34_isset_overloading/1_instance_34_isset_overloading/1_instance_34_isset_overloading.json old mode 100644 new mode 100755 index e6c8bee..5d52600 --- a/PHP/34_isset_overloading/1_instance_34_isset_overloading/1_instance_34_isset_overloading.json +++ b/PHP/34_isset_overloading/1_instance_34_isset_overloading/1_instance_34_isset_overloading.json @@ -1,37 +1,38 @@ -{ - "code": { - "path": "./1_instance_34_isset_overloading.php", - "injection_skeleton_broken": false - }, - "discovery": { - "rule": "./1_instance_34_isset_overloading.sc", - "method": "joern", - "rule_accuracy": "FP", - "notes": null - }, - "compile": { - "binary": "./1_instance_34_isset_overloading.bash", - "instruction": null, - "dependencies": null - }, - "expectation": { - "type": "xss", - "sink_file": "./1_instance_34_isset_overloading.php", - "sink_line": 9, - "source_file": "./1_instance_34_isset_overloading.php", - "source_line": 19, - "expectation": true - }, - "properties": { - "category": "S0", - "feature_vs_internal_api": "FEATURE", - "input_sanitizer": false, - "source_and_sink": false, - "negative_test_case": false - }, - "remediation": { - "notes": "", - "transformation": null, - "modeling_rule": null - } +{ + "description": "This instance shows, that `__isset` is called, when the function `isset()` is called.", + "code": { + "path": "./1_instance_34_isset_overloading.php", + "injection_skeleton_broken": true + }, + "discovery": { + "rule": "../34_isset_overloading.sc", + "method": "joern", + "rule_accuracy": "FP", + "notes": "../docs/discovery_notes.md" + }, + "compile": { + "binary": "./1_instance_34_isset_overloading.bash", + "instruction": null, + "dependencies": null + }, + "expectation": { + "type": "xss", + "sink_file": "./1_instance_34_isset_overloading.php", + "sink_line": 6, + "source_file": "./1_instance_34_isset_overloading.php", + "source_line": 15, + "expectation": true + }, + "properties": { + "category": "S0", + "feature_vs_internal_api": "FEATURE", + "input_sanitizer": false, + "source_and_sink": false, + "negative_test_case": false + }, + "remediation": { + "notes": "", + "transformation": null, + "modeling_rule": null + } } \ No newline at end of file diff --git a/PHP/34_isset_overloading/1_instance_34_isset_overloading/1_instance_34_isset_overloading.php b/PHP/34_isset_overloading/1_instance_34_isset_overloading/1_instance_34_isset_overloading.php old mode 100644 new mode 100755 index 7919218..e2b84de --- a/PHP/34_isset_overloading/1_instance_34_isset_overloading/1_instance_34_isset_overloading.php +++ b/PHP/34_isset_overloading/1_instance_34_isset_overloading/1_instance_34_isset_overloading.php @@ -1,26 +1,20 @@ -x' set?\n"; - return true; - } - - public function setx($b) - { - $this->x = $b; - } -} - -$b = $_GET["p1"]; -$obj = new PropertyTest; -$obj->setx($b); - -// XSS vulnerability in the function __isset() -if (isset($obj->x)) { - echo "isset called"; -} \ No newline at end of file +x; // sink + return true; + } + + public function setx($b) { + $this->x = $b; + } +} + +$b = $_GET["p1"]; // source +$obj = new PropertyTest; +$obj->setx($b); + +// XSS vulnerability in the function __isset() +isset($obj->x); diff --git a/PHP/34_isset_overloading/1_instance_34_isset_overloading/1_instance_34_isset_overloading.sc b/PHP/34_isset_overloading/1_instance_34_isset_overloading/1_instance_34_isset_overloading.sc deleted file mode 100644 index 65d9552..0000000 --- a/PHP/34_isset_overloading/1_instance_34_isset_overloading/1_instance_34_isset_overloading.sc +++ /dev/null @@ -1,7 +0,0 @@ -@main def main(name : String): Unit = { - importCpg(name) - def methods3 = cpg.method.name("__isset").name.l - val x34 = (name, "34_isset_overloading_iall", cpg.call("NEW").argument.filter{x => methods3.contains(x.code.toLowerCase)}.location.toJson); - println(x34) - delete; -} \ No newline at end of file diff --git a/PHP/34_isset_overloading/2_instance_34_isset_overloading/2_instance_34_isset_overloading.bash b/PHP/34_isset_overloading/2_instance_34_isset_overloading/2_instance_34_isset_overloading.bash new file mode 100755 index 0000000..a3c7b70 --- /dev/null +++ b/PHP/34_isset_overloading/2_instance_34_isset_overloading/2_instance_34_isset_overloading.bash @@ -0,0 +1,41 @@ + +$_main: + ; (lines=12, args=0, vars=2, tmps=8) + ; (before optimizer) + ; /.../PHP/34_isset_overloading/2_instance_34_isset_overloading/2_instance_34_isset_overloading.php:1-20 + ; return [] RANGE[0..0] +0000 T2 = FETCH_R (global) string("_GET") +0001 T3 = FETCH_DIM_R T2 string("p1") +0002 ASSIGN CV0($b) T3 +0003 V5 = NEW 0 string("PropertyTest") +0004 DO_FCALL +0005 ASSIGN CV1($obj) V5 +0006 INIT_METHOD_CALL 1 CV1($obj) string("setx") +0007 SEND_VAR_EX CV0($b) 1 +0008 DO_FCALL +0009 T9 = ISSET_ISEMPTY_PROP_OBJ (empty) CV1($obj) string("x") +0010 FREE T9 +0011 RETURN int(1) +LIVE RANGES: + 5: 0004 - 0005 (new) + +PropertyTest::__isset: + ; (lines=5, args=1, vars=1, tmps=1) + ; (before optimizer) + ; /.../PHP/34_isset_overloading/2_instance_34_isset_overloading/2_instance_34_isset_overloading.php:5-8 + ; return [] RANGE[0..0] +0000 CV0($name) = RECV 1 +0001 T1 = FETCH_OBJ_R THIS string("x") +0002 ECHO T1 +0003 RETURN bool(true) +0004 RETURN null + +PropertyTest::setx: + ; (lines=4, args=1, vars=1, tmps=1) + ; (before optimizer) + ; /.../PHP/34_isset_overloading/2_instance_34_isset_overloading/2_instance_34_isset_overloading.php:10-12 + ; return [] RANGE[0..0] +0000 CV0($b) = RECV 1 +0001 ASSIGN_OBJ THIS string("x") +0002 OP_DATA CV0($b) +0003 RETURN null diff --git a/PHP/34_isset_overloading/2_instance_34_isset_overloading/2_instance_34_isset_overloading.json b/PHP/34_isset_overloading/2_instance_34_isset_overloading/2_instance_34_isset_overloading.json old mode 100644 new mode 100755 index 3d767fb..fbf7e70 --- a/PHP/34_isset_overloading/2_instance_34_isset_overloading/2_instance_34_isset_overloading.json +++ b/PHP/34_isset_overloading/2_instance_34_isset_overloading/2_instance_34_isset_overloading.json @@ -1,37 +1,38 @@ -{ - "code": { - "path": "./2_instance_34_isset_overloading.php", - "injection_skeleton_broken": false - }, - "discovery": { - "rule": "./2_instance_34_isset_overloading.sc", - "method": "joern", - "rule_accuracy": "FP", - "notes": null - }, - "compile": { - "binary": "./2_instance_34_isset_overloading.bash", - "instruction": null, - "dependencies": null - }, - "expectation": { - "type": "xss", - "sink_file": "./2_instance_34_isset_overloading.php", - "sink_line": 9, - "source_file": "./2_instance_34_isset_overloading.php", - "source_line": 19, - "expectation": true - }, - "properties": { - "category": "S0", - "feature_vs_internal_api": "FEATURE", - "input_sanitizer": false, - "source_and_sink": false, - "negative_test_case": false - }, - "remediation": { - "notes": "", - "transformation": null, - "modeling_rule": null - } +{ + "description": "This instance shows, that `__isset` is also invoked when `empty()` is called.", + "code": { + "path": "./2_instance_34_isset_overloading.php", + "injection_skeleton_broken": true + }, + "discovery": { + "rule": "../34_isset_overloading.sc", + "method": "joern", + "rule_accuracy": "FP", + "notes": "../docs/discovery_notes.md" + }, + "compile": { + "binary": "./2_instance_34_isset_overloading.bash", + "instruction": null, + "dependencies": null + }, + "expectation": { + "type": "xss", + "sink_file": "./2_instance_34_isset_overloading.php", + "sink_line": 6, + "source_file": "./2_instance_34_isset_overloading.php", + "source_line": 15, + "expectation": true + }, + "properties": { + "category": "S0", + "feature_vs_internal_api": "FEATURE", + "input_sanitizer": false, + "source_and_sink": false, + "negative_test_case": false + }, + "remediation": { + "notes": "", + "transformation": null, + "modeling_rule": null + } } \ No newline at end of file diff --git a/PHP/34_isset_overloading/2_instance_34_isset_overloading/2_instance_34_isset_overloading.php b/PHP/34_isset_overloading/2_instance_34_isset_overloading/2_instance_34_isset_overloading.php old mode 100644 new mode 100755 index 4cca721..edbd776 --- a/PHP/34_isset_overloading/2_instance_34_isset_overloading/2_instance_34_isset_overloading.php +++ b/PHP/34_isset_overloading/2_instance_34_isset_overloading/2_instance_34_isset_overloading.php @@ -1,26 +1,20 @@ -x' set?\n"; - return true; - } - - public function setx($b) - { - $this->x = $b; - } -} - -$b = $_GET["p1"]; -$obj = new PropertyTest; -$obj->setx($b); - -// XSS vulnerability in the function __isset() -if (empty($obj->x)) { - echo "isset called"; -} \ No newline at end of file +x; // sink + return true; + } + + public function setx($b) { + $this->x = $b; + } +} + +$b = $_GET["p1"]; // source +$obj = new PropertyTest; +$obj->setx($b); + +// XSS vulnerability in the function __isset() +empty($obj->x); \ No newline at end of file diff --git a/PHP/34_isset_overloading/2_instance_34_isset_overloading/2_instance_34_isset_overloading.sc b/PHP/34_isset_overloading/2_instance_34_isset_overloading/2_instance_34_isset_overloading.sc deleted file mode 100644 index ffe150c..0000000 --- a/PHP/34_isset_overloading/2_instance_34_isset_overloading/2_instance_34_isset_overloading.sc +++ /dev/null @@ -1,7 +0,0 @@ -@main def main(name : String): Unit = { - importCpg(name) - def methods3 = cpg.method.name("__set").name.l - val x34 = (name, "34_isset_overloading_iall", cpg.call("NEW").argument.filter{x => methods3.contains(x.code.toLowerCase)}.location.toJson); - println(x34) - delete; -} \ No newline at end of file diff --git a/PHP/34_isset_overloading/34_isset_overloading.json b/PHP/34_isset_overloading/34_isset_overloading.json old mode 100644 new mode 100755 index cc62f88..16b236b --- a/PHP/34_isset_overloading/34_isset_overloading.json +++ b/PHP/34_isset_overloading/34_isset_overloading.json @@ -1,14 +1,17 @@ -{ - "name": "Isset Overloading", - "description": "", - "family": "code_pattern_php", - "tags": [ - "sast", - "php", - "php_v7.4.9" - ], - "instances": [ - "./1_instance_34_isset_overloading/1_instance_34_isset_overloading.json" - ], - "version": "v1.0" +{ + "name": "Isset Overloading", + "description": "./docs/description.md", + "family": "code_pattern_php", + "tags": [ + "sast", + "php", + "php_v7.4.9" + ], + "instances": [ + "./1_instance_34_isset_overloading/1_instance_34_isset_overloading.json", + "./2_instance_34_isset_overloading/2_instance_34_isset_overloading.json", + "./3_instance_34_isset_overloading/3_instance_34_isset_overloading.json", + "./4_instance_34_isset_overloading/4_instance_34_isset_overloading.json" + ], + "version": "v1.0" } \ No newline at end of file diff --git a/PHP/34_isset_overloading/34_isset_overloading.sc b/PHP/34_isset_overloading/34_isset_overloading.sc new file mode 100755 index 0000000..682f62e --- /dev/null +++ b/PHP/34_isset_overloading/34_isset_overloading.sc @@ -0,0 +1,8 @@ +@main def main(name : String): Unit = { + importCpg(name) + // Get all classes (lowercase) where `__isset` is defined + def methodClasses = cpg.method.name("__isset").astParentFullName.l + val x34 = (name, "34_isset_overloading_iall", cpg.call("NEW").argument.filter{x => methodClasses.contains(x.code.toLowerCase)}.location.toJson); + println(x34) + delete; +} \ No newline at end of file diff --git a/PHP/34_isset_overloading/3_instance_34_isset_overloading/3_instance_34_isset_overloading.bash b/PHP/34_isset_overloading/3_instance_34_isset_overloading/3_instance_34_isset_overloading.bash new file mode 100755 index 0000000..bfe2604 --- /dev/null +++ b/PHP/34_isset_overloading/3_instance_34_isset_overloading/3_instance_34_isset_overloading.bash @@ -0,0 +1,41 @@ + +$_main: + ; (lines=12, args=0, vars=2, tmps=8) + ; (before optimizer) + ; /.../PHP/34_isset_overloading/3_instance_34_isset_overloading/3_instance_34_isset_overloading.php:1-22 + ; return [] RANGE[0..0] +0000 T2 = FETCH_R (global) string("_GET") +0001 T3 = FETCH_DIM_R T2 string("p1") +0002 ASSIGN CV0($b) T3 +0003 V5 = NEW 0 string("PropertyTest") +0004 DO_FCALL +0005 ASSIGN CV1($obj) V5 +0006 INIT_METHOD_CALL 1 CV1($obj) string("setx") +0007 SEND_VAR_EX CV0($b) 1 +0008 DO_FCALL +0009 T9 = ISSET_ISEMPTY_PROP_OBJ (isset) CV1($obj) string("var") +0010 FREE T9 +0011 RETURN int(1) +LIVE RANGES: + 5: 0004 - 0005 (new) + +PropertyTest::__isset: + ; (lines=5, args=1, vars=1, tmps=1) + ; (before optimizer) + ; /.../PHP/34_isset_overloading/3_instance_34_isset_overloading/3_instance_34_isset_overloading.php:5-8 + ; return [] RANGE[0..0] +0000 CV0($name) = RECV 1 +0001 T1 = FETCH_OBJ_R THIS string("x") +0002 ECHO T1 +0003 RETURN bool(true) +0004 RETURN null + +PropertyTest::setx: + ; (lines=4, args=1, vars=1, tmps=1) + ; (before optimizer) + ; /.../PHP/34_isset_overloading/3_instance_34_isset_overloading/3_instance_34_isset_overloading.php:10-12 + ; return [] RANGE[0..0] +0000 CV0($b) = RECV 1 +0001 ASSIGN_OBJ THIS string("x") +0002 OP_DATA CV0($b) +0003 RETURN null diff --git a/PHP/34_isset_overloading/3_instance_34_isset_overloading/3_instance_34_isset_overloading.json b/PHP/34_isset_overloading/3_instance_34_isset_overloading/3_instance_34_isset_overloading.json new file mode 100755 index 0000000..cafbbe8 --- /dev/null +++ b/PHP/34_isset_overloading/3_instance_34_isset_overloading/3_instance_34_isset_overloading.json @@ -0,0 +1,38 @@ +{ + "description": "This instance shows, that `__isset` is invoked when trying to access non existant variables.", + "code": { + "path": "./3_instance_34_isset_overloading.php", + "injection_skeleton_broken": true + }, + "expectation": { + "type": "xss", + "sink_file": "./3_instance_34_isset_overloading.php", + "sink_line": 6, + "source_file": "./3_instance_34_isset_overloading.php", + "source_line": 15, + "expectation": true + }, + "compile": { + "binary": "./3_instance_34_isset_overloading.bash", + "instruction": null, + "dependencies": null + }, + "discovery": { + "rule": "../34_isset_overloading.sc", + "method": "joern", + "rule_accuracy": "FP", + "notes": "../docs/discovery_notes.md" + }, + "properties": { + "category": "S0", + "feature_vs_internal_api": "FEATURE", + "input_sanitizer": false, + "source_and_sink": false, + "negative_test_case": false + }, + "remediation": { + "notes": "", + "transformation": null, + "modeling_rule": null + } +} \ No newline at end of file diff --git a/PHP/34_isset_overloading/3_instance_34_isset_overloading/3_instance_34_isset_overloading.php b/PHP/34_isset_overloading/3_instance_34_isset_overloading/3_instance_34_isset_overloading.php new file mode 100755 index 0000000..a722187 --- /dev/null +++ b/PHP/34_isset_overloading/3_instance_34_isset_overloading/3_instance_34_isset_overloading.php @@ -0,0 +1,21 @@ +x; // sink + return true; + } + + public function setx($b) { + $this->x = $b; + } +} + +$b = $_GET["p1"]; // source +$obj = new PropertyTest; +$obj->setx($b); + +// XSS vulnerability in the function __isset() +// __isset() is called, because $obj->var does not exist +isset($obj->var); diff --git a/PHP/34_isset_overloading/4_instance_34_isset_overloading/4_instance_34_isset_overloading.bash b/PHP/34_isset_overloading/4_instance_34_isset_overloading/4_instance_34_isset_overloading.bash new file mode 100755 index 0000000..cbf02e8 --- /dev/null +++ b/PHP/34_isset_overloading/4_instance_34_isset_overloading/4_instance_34_isset_overloading.bash @@ -0,0 +1,30 @@ + +$_main: + ; (lines=11, args=0, vars=2, tmps=8) + ; (before optimizer) + ; /.../PHP/34_isset_overloading/4_instance_34_isset_overloading/4_instance_34_isset_overloading.php:1-17 + ; return [] RANGE[0..0] +0000 T2 = FETCH_R (global) string("_GET") +0001 T3 = FETCH_DIM_R T2 string("p1") +0002 ASSIGN CV0($b) T3 +0003 V5 = NEW 0 string("PropertyTest") +0004 DO_FCALL +0005 ASSIGN CV1($obj) V5 +0006 ASSIGN_OBJ CV1($obj) string("x") +0007 OP_DATA CV0($b) +0008 T9 = ISSET_ISEMPTY_PROP_OBJ (isset) CV1($obj) string("x") +0009 FREE T9 +0010 RETURN int(1) +LIVE RANGES: + 5: 0004 - 0005 (new) + +PropertyTest::__isset: + ; (lines=5, args=1, vars=1, tmps=1) + ; (before optimizer) + ; /.../PHP/34_isset_overloading/4_instance_34_isset_overloading/4_instance_34_isset_overloading.php:6-9 + ; return [] RANGE[0..0] +0000 CV0($name) = RECV 1 +0001 T1 = FETCH_OBJ_R THIS string("x") +0002 ECHO T1 +0003 RETURN bool(true) +0004 RETURN null diff --git a/PHP/34_isset_overloading/4_instance_34_isset_overloading/4_instance_34_isset_overloading.json b/PHP/34_isset_overloading/4_instance_34_isset_overloading/4_instance_34_isset_overloading.json new file mode 100755 index 0000000..1e7fc6f --- /dev/null +++ b/PHP/34_isset_overloading/4_instance_34_isset_overloading/4_instance_34_isset_overloading.json @@ -0,0 +1,38 @@ +{ + "description": "This instance is not vulnerable. As the object has the property `x`, `__isset` will not be called.", + "code": { + "path": "./4_instance_34_isset_overloading.php", + "injection_skeleton_broken": true + }, + "expectation": { + "type": "xss", + "sink_file": "./4_instance_34_isset_overloading.php", + "sink_line": 7, + "source_file": "./4_instance_34_isset_overloading.php", + "source_line": 12, + "expectation": false + }, + "compile": { + "binary": "./4_instance_34_isset_overloading.bash", + "instruction": null, + "dependencies": null + }, + "discovery": { + "rule": "../34_isset_overloading.sc", + "method": "joern", + "rule_accuracy": "FP", + "notes": "../docs/discovery_notes.md" + }, + "properties": { + "category": "S0", + "feature_vs_internal_api": "FEATURE", + "input_sanitizer": false, + "source_and_sink": false, + "negative_test_case": false + }, + "remediation": { + "notes": "", + "transformation": null, + "modeling_rule": null + } +} \ No newline at end of file diff --git a/PHP/34_isset_overloading/4_instance_34_isset_overloading/4_instance_34_isset_overloading.php b/PHP/34_isset_overloading/4_instance_34_isset_overloading/4_instance_34_isset_overloading.php new file mode 100755 index 0000000..6fe626e --- /dev/null +++ b/PHP/34_isset_overloading/4_instance_34_isset_overloading/4_instance_34_isset_overloading.php @@ -0,0 +1,17 @@ +x; // sink + return true; + } +} + +$b = $_GET["p1"]; // source +$obj = new PropertyTest; +$obj->x = $b; + +// __isset() is not called, because $x is public +isset($obj->x); \ No newline at end of file diff --git a/PHP/34_isset_overloading/Pattern Isset overloading.md b/PHP/34_isset_overloading/Pattern Isset overloading.md deleted file mode 100644 index a245a2c..0000000 --- a/PHP/34_isset_overloading/Pattern Isset overloading.md +++ /dev/null @@ -1,19 +0,0 @@ -# Pattern: Isset Overloading - -## Category - -Objects - -## Definition - -PHP allows developers to supply a `__isset` function that is used by the PHP calls `isset` and `empty`. - -## Instances - -### Instance 1 - -uses `__isset` via `isset` function - -### Instance 2 - -uses `__isset` via `empty` function \ No newline at end of file diff --git a/PHP/34_isset_overloading/README.md b/PHP/34_isset_overloading/README.md new file mode 100755 index 0000000..b7530ed --- /dev/null +++ b/PHP/34_isset_overloading/README.md @@ -0,0 +1,539 @@ +[//]: # (This file is automatically generated. If you wish to make any changes, please use the JSON files and regenerate this file using the tpframework.) + +# Isset Overloading + +Tags: sast, php, php_v7.4.9 + +Version: v1.0 + +## Description + +In many object-oriented programming languages overloading provides the ability of having multiple methods with the same name but different quantities and types of arguments. In PHP however [Overloading](https://www.php.net/manual/en/language.oop5.overloading.php) describes the concept of dynamically creating properties and methods. This pattern targets the usage of property overloading for `__isset()`. This method is triggered by calling isset() or empty() on inaccessible (protected or private) or non-existing properties. + +## Overview + +| Instances | has discovery rule | discovery method | rule successfull | +|---------------------------|----------------------|--------------------|--------------------| +| [1 Instance](#1-instance) | yes | joern | yes | +| [2 Instance](#2-instance) | yes | joern | yes | +| [3 Instance](#3-instance) | yes | joern | yes | +| [4 Instance](#4-instance) | yes | joern | yes | + +
+ + +## 1 Instance + + +This instance shows, that `__isset` is called, when the function `isset()` is called. + +### Code + +```PHP +x; // sink + return true; + } + + public function setx($b) { + $this->x = $b; + } +} + +$b = $_GET["p1"]; // source +$obj = new PropertyTest; +$obj->setx($b); + +// XSS vulnerability in the function __isset() +isset($obj->x); +``` + +### Instance Properties + +| category | feature_vs_internal_api | input_sanitizer | negative_test_case | source_and_sink | +|------------|---------------------------|-------------------|----------------------|-------------------| +| S0 | FEATURE | no | no | no | + +
+ +More + +
+ + +### Compile + + +```bash +$_main: + ; (lines=12, args=0, vars=2, tmps=8) + ; (before optimizer) + ; /.../PHP/34_isset_overloading/1_instance_34_isset_overloading/1_instance_34_isset_overloading.php:1-21 + ; return [] RANGE[0..0] +0000 T2 = FETCH_R (global) string("_GET") +0001 T3 = FETCH_DIM_R T2 string("p1") +0002 ASSIGN CV0($b) T3 +0003 V5 = NEW 0 string("PropertyTest") +0004 DO_FCALL +0005 ASSIGN CV1($obj) V5 +0006 INIT_METHOD_CALL 1 CV1($obj) string("setx") +0007 SEND_VAR_EX CV0($b) 1 +0008 DO_FCALL +0009 T9 = ISSET_ISEMPTY_PROP_OBJ (isset) CV1($obj) string("x") +0010 FREE T9 +0011 RETURN int(1) +LIVE RANGES: + 5: 0004 - 0005 (new) + +PropertyTest::__isset: + ; (lines=5, args=1, vars=1, tmps=1) + ; (before optimizer) + ; /.../PHP/34_isset_overloading/1_instance_34_isset_overloading/1_instance_34_isset_overloading.php:5-8 + ; return [] RANGE[0..0] +0000 CV0($name) = RECV 1 +0001 T1 = FETCH_OBJ_R THIS string("x") +0002 ECHO T1 +0003 RETURN bool(true) +0004 RETURN null + +PropertyTest::setx: + ; (lines=4, args=1, vars=1, tmps=1) + ; (before optimizer) + ; /.../PHP/34_isset_overloading/1_instance_34_isset_overloading/1_instance_34_isset_overloading.php:10-12 + ; return [] RANGE[0..0] +0000 CV0($b) = RECV 1 +0001 ASSIGN_OBJ THIS string("x") +0002 OP_DATA CV0($b) +0003 RETURN null +``` + +
+ +
+ + +### Discovery + + +The discovery rule first gets all class names, where the method `__isset` is defined. +Afterwards it collects all `NEW` calles and filters them to see which of the instanciated objects has the method `__isset` defined. +The rule would be perfect, if we could additionally check, if one of the created objects invokes the `__isset` method within its lifetime. + +```scala +// Get all classes (lowercase) where `__isset` is defined +def methodClasses = cpg.method.name("__isset").astParentFullName.l +val x34 = (name, "34_isset_overloading_iall", cpg.call("NEW").argument.filter{x => methodClasses.contains(x.code.toLowerCase)}.location.toJson); +``` + +| discovery method | expected accuracy | +|--------------------|---------------------| +| joern | FP | + +
+ +
+ + +### Measurement + + +| Tool | Comm_1 | Comm_2 | phpSAFE | Progpilot | RIPS | WAP | Ground Truth | +|-------------|----------|----------|-----------|-------------|--------|-------|----------------| +| 08 Jun 2021 | no | no | no | no | no | no | yes | +| 17 May 2023 | no | no | | | | | yes | + +
+ +
+ +
+ +
+ + +## 2 Instance + + +This instance shows, that `__isset` is also invoked when `empty()` is called. + +### Code + +```PHP +x; // sink + return true; + } + + public function setx($b) { + $this->x = $b; + } +} + +$b = $_GET["p1"]; // source +$obj = new PropertyTest; +$obj->setx($b); + +// XSS vulnerability in the function __isset() +empty($obj->x); +``` + +### Instance Properties + +| category | feature_vs_internal_api | input_sanitizer | negative_test_case | source_and_sink | +|------------|---------------------------|-------------------|----------------------|-------------------| +| S0 | FEATURE | no | no | no | + +
+ +More + +
+ + +### Compile + + +```bash +$_main: + ; (lines=12, args=0, vars=2, tmps=8) + ; (before optimizer) + ; /.../PHP/34_isset_overloading/2_instance_34_isset_overloading/2_instance_34_isset_overloading.php:1-20 + ; return [] RANGE[0..0] +0000 T2 = FETCH_R (global) string("_GET") +0001 T3 = FETCH_DIM_R T2 string("p1") +0002 ASSIGN CV0($b) T3 +0003 V5 = NEW 0 string("PropertyTest") +0004 DO_FCALL +0005 ASSIGN CV1($obj) V5 +0006 INIT_METHOD_CALL 1 CV1($obj) string("setx") +0007 SEND_VAR_EX CV0($b) 1 +0008 DO_FCALL +0009 T9 = ISSET_ISEMPTY_PROP_OBJ (empty) CV1($obj) string("x") +0010 FREE T9 +0011 RETURN int(1) +LIVE RANGES: + 5: 0004 - 0005 (new) + +PropertyTest::__isset: + ; (lines=5, args=1, vars=1, tmps=1) + ; (before optimizer) + ; /.../PHP/34_isset_overloading/2_instance_34_isset_overloading/2_instance_34_isset_overloading.php:5-8 + ; return [] RANGE[0..0] +0000 CV0($name) = RECV 1 +0001 T1 = FETCH_OBJ_R THIS string("x") +0002 ECHO T1 +0003 RETURN bool(true) +0004 RETURN null + +PropertyTest::setx: + ; (lines=4, args=1, vars=1, tmps=1) + ; (before optimizer) + ; /.../PHP/34_isset_overloading/2_instance_34_isset_overloading/2_instance_34_isset_overloading.php:10-12 + ; return [] RANGE[0..0] +0000 CV0($b) = RECV 1 +0001 ASSIGN_OBJ THIS string("x") +0002 OP_DATA CV0($b) +0003 RETURN null +``` + +
+ +
+ + +### Discovery + + +The discovery rule first gets all class names, where the method `__isset` is defined. +Afterwards it collects all `NEW` calles and filters them to see which of the instanciated objects has the method `__isset` defined. +The rule would be perfect, if we could additionally check, if one of the created objects invokes the `__isset` method within its lifetime. + +```scala +// Get all classes (lowercase) where `__isset` is defined +def methodClasses = cpg.method.name("__isset").astParentFullName.l +val x34 = (name, "34_isset_overloading_iall", cpg.call("NEW").argument.filter{x => methodClasses.contains(x.code.toLowerCase)}.location.toJson); +``` + +| discovery method | expected accuracy | +|--------------------|---------------------| +| joern | FP | + +
+ +
+ + +### Measurement + + +| Tool | Comm_1 | Comm_2 | phpSAFE | Progpilot | RIPS | WAP | Ground Truth | +|-------------|----------|----------|-----------|-------------|--------|-------|----------------| +| 08 Jun 2021 | no | no | no | no | no | no | yes | +| 17 May 2023 | no | no | | | | | yes | + +
+ +
+ +
+ +
+ + +## 3 Instance + + +This instance shows, that `__isset` is invoked when trying to access non existant variables. + +### Code + +```PHP +x; // sink + return true; + } + + public function setx($b) { + $this->x = $b; + } +} + +$b = $_GET["p1"]; // source +$obj = new PropertyTest; +$obj->setx($b); + +// XSS vulnerability in the function __isset() +// __isset() is called, because $obj->var does not exist +isset($obj->var); +``` + +### Instance Properties + +| category | feature_vs_internal_api | input_sanitizer | negative_test_case | source_and_sink | +|------------|---------------------------|-------------------|----------------------|-------------------| +| S0 | FEATURE | no | no | no | + +
+ +More + +
+ + +### Compile + + +```bash +$_main: + ; (lines=12, args=0, vars=2, tmps=8) + ; (before optimizer) + ; /.../PHP/34_isset_overloading/3_instance_34_isset_overloading/3_instance_34_isset_overloading.php:1-22 + ; return [] RANGE[0..0] +0000 T2 = FETCH_R (global) string("_GET") +0001 T3 = FETCH_DIM_R T2 string("p1") +0002 ASSIGN CV0($b) T3 +0003 V5 = NEW 0 string("PropertyTest") +0004 DO_FCALL +0005 ASSIGN CV1($obj) V5 +0006 INIT_METHOD_CALL 1 CV1($obj) string("setx") +0007 SEND_VAR_EX CV0($b) 1 +0008 DO_FCALL +0009 T9 = ISSET_ISEMPTY_PROP_OBJ (isset) CV1($obj) string("var") +0010 FREE T9 +0011 RETURN int(1) +LIVE RANGES: + 5: 0004 - 0005 (new) + +PropertyTest::__isset: + ; (lines=5, args=1, vars=1, tmps=1) + ; (before optimizer) + ; /.../PHP/34_isset_overloading/3_instance_34_isset_overloading/3_instance_34_isset_overloading.php:5-8 + ; return [] RANGE[0..0] +0000 CV0($name) = RECV 1 +0001 T1 = FETCH_OBJ_R THIS string("x") +0002 ECHO T1 +0003 RETURN bool(true) +0004 RETURN null + +PropertyTest::setx: + ; (lines=4, args=1, vars=1, tmps=1) + ; (before optimizer) + ; /.../PHP/34_isset_overloading/3_instance_34_isset_overloading/3_instance_34_isset_overloading.php:10-12 + ; return [] RANGE[0..0] +0000 CV0($b) = RECV 1 +0001 ASSIGN_OBJ THIS string("x") +0002 OP_DATA CV0($b) +0003 RETURN null +``` + +
+ +
+ + +### Discovery + + +The discovery rule first gets all class names, where the method `__isset` is defined. +Afterwards it collects all `NEW` calles and filters them to see which of the instanciated objects has the method `__isset` defined. +The rule would be perfect, if we could additionally check, if one of the created objects invokes the `__isset` method within its lifetime. + +```scala +// Get all classes (lowercase) where `__isset` is defined +def methodClasses = cpg.method.name("__isset").astParentFullName.l +val x34 = (name, "34_isset_overloading_iall", cpg.call("NEW").argument.filter{x => methodClasses.contains(x.code.toLowerCase)}.location.toJson); +``` + +| discovery method | expected accuracy | +|--------------------|---------------------| +| joern | FP | + +
+ +
+ + +### Measurement + + +| Tool | Comm_1 | Comm_2 | phpSAFE | Progpilot | RIPS | WAP | Ground Truth | +|-------------|----------|----------|-----------|-------------|--------|-------|----------------| +| 08 Jun 2021 | no | no | no | no | no | no | yes | +| 17 May 2023 | no | no | | | | | yes | + +
+ +
+ +
+ +
+ + +## 4 Instance + + +This instance is not vulnerable. As the object has the property `x`, `__isset` will not be called. + +### Code + +```PHP +x; // sink + return true; + } +} + +$b = $_GET["p1"]; // source +$obj = new PropertyTest; +$obj->x = $b; + +// __isset() is not called, because $x is public +isset($obj->x); +``` + +### Instance Properties + +| category | feature_vs_internal_api | input_sanitizer | negative_test_case | source_and_sink | +|------------|---------------------------|-------------------|----------------------|-------------------| +| S0 | FEATURE | no | no | no | + +
+ +More + +
+ + +### Compile + + +```bash +$_main: + ; (lines=11, args=0, vars=2, tmps=8) + ; (before optimizer) + ; /.../PHP/34_isset_overloading/4_instance_34_isset_overloading/4_instance_34_isset_overloading.php:1-17 + ; return [] RANGE[0..0] +0000 T2 = FETCH_R (global) string("_GET") +0001 T3 = FETCH_DIM_R T2 string("p1") +0002 ASSIGN CV0($b) T3 +0003 V5 = NEW 0 string("PropertyTest") +0004 DO_FCALL +0005 ASSIGN CV1($obj) V5 +0006 ASSIGN_OBJ CV1($obj) string("x") +0007 OP_DATA CV0($b) +0008 T9 = ISSET_ISEMPTY_PROP_OBJ (isset) CV1($obj) string("x") +0009 FREE T9 +0010 RETURN int(1) +LIVE RANGES: + 5: 0004 - 0005 (new) + +PropertyTest::__isset: + ; (lines=5, args=1, vars=1, tmps=1) + ; (before optimizer) + ; /.../PHP/34_isset_overloading/4_instance_34_isset_overloading/4_instance_34_isset_overloading.php:6-9 + ; return [] RANGE[0..0] +0000 CV0($name) = RECV 1 +0001 T1 = FETCH_OBJ_R THIS string("x") +0002 ECHO T1 +0003 RETURN bool(true) +0004 RETURN null +``` + +
+ +
+ + +### Discovery + + +The discovery rule first gets all class names, where the method `__isset` is defined. +Afterwards it collects all `NEW` calles and filters them to see which of the instanciated objects has the method `__isset` defined. +The rule would be perfect, if we could additionally check, if one of the created objects invokes the `__isset` method within its lifetime. + +```scala +// Get all classes (lowercase) where `__isset` is defined +def methodClasses = cpg.method.name("__isset").astParentFullName.l +val x34 = (name, "34_isset_overloading_iall", cpg.call("NEW").argument.filter{x => methodClasses.contains(x.code.toLowerCase)}.location.toJson); +``` + +| discovery method | expected accuracy | +|--------------------|---------------------| +| joern | FP | + +
+ +
+ + +### Measurement + + +| Tool | Comm_1 | Comm_2 | phpSAFE | Progpilot | RIPS | WAP | Ground Truth | +|-------------|----------|----------|-----------|-------------|--------|-------|----------------| +| 08 Jun 2021 | yes | yes | yes | yes | yes | yes | no | +| 17 May 2023 | no | no | | | | | no | + +
+ +
+ +
diff --git a/PHP/34_isset_overloading/docs/description.md b/PHP/34_isset_overloading/docs/description.md new file mode 100755 index 0000000..87babe4 --- /dev/null +++ b/PHP/34_isset_overloading/docs/description.md @@ -0,0 +1 @@ +In many object-oriented programming languages overloading provides the ability of having multiple methods with the same name but different quantities and types of arguments. In PHP however [Overloading](https://www.php.net/manual/en/language.oop5.overloading.php) describes the concept of dynamically creating properties and methods. This pattern targets the usage of property overloading for `__isset()`. This method is triggered by calling isset() or empty() on inaccessible (protected or private) or non-existing properties. \ No newline at end of file diff --git a/PHP/34_isset_overloading/docs/discovery_notes.md b/PHP/34_isset_overloading/docs/discovery_notes.md new file mode 100755 index 0000000..6946211 --- /dev/null +++ b/PHP/34_isset_overloading/docs/discovery_notes.md @@ -0,0 +1,3 @@ +The discovery rule first gets all class names, where the method `__isset` is defined. +Afterwards it collects all `NEW` calles and filters them to see which of the instanciated objects has the method `__isset` defined. +The rule would be perfect, if we could additionally check, if one of the created objects invokes the `__isset` method within its lifetime. \ No newline at end of file diff --git a/PHP/35_unset_overloading/1_instance_35_unset_overloading/1_instance_35_unset_overloading.bash b/PHP/35_unset_overloading/1_instance_35_unset_overloading/1_instance_35_unset_overloading.bash new file mode 100755 index 0000000..5bb8327 --- /dev/null +++ b/PHP/35_unset_overloading/1_instance_35_unset_overloading/1_instance_35_unset_overloading.bash @@ -0,0 +1,39 @@ + +$_main: + ; (lines=11, args=0, vars=2, tmps=7) + ; (before optimizer) + ; /.../PHP/35_unset_overloading/1_instance_35_unset_overloading/1_instance_35_unset_overloading.php:1-18 + ; return [] RANGE[0..0] +0000 T2 = FETCH_R (global) string("_GET") +0001 T3 = FETCH_DIM_R T2 string("p1") +0002 ASSIGN CV0($b) T3 +0003 V5 = NEW 0 string("PropertyTest") +0004 DO_FCALL +0005 ASSIGN CV1($obj) V5 +0006 INIT_METHOD_CALL 1 CV1($obj) string("setx") +0007 SEND_VAR_EX CV0($b) 1 +0008 DO_FCALL +0009 UNSET_OBJ CV1($obj) string("x") +0010 RETURN int(1) +LIVE RANGES: + 5: 0004 - 0005 (new) + +PropertyTest::setx: + ; (lines=4, args=1, vars=1, tmps=1) + ; (before optimizer) + ; /.../PHP/35_unset_overloading/1_instance_35_unset_overloading/1_instance_35_unset_overloading.php:5-7 + ; return [] RANGE[0..0] +0000 CV0($b) = RECV 1 +0001 ASSIGN_OBJ THIS string("x") +0002 OP_DATA CV0($b) +0003 RETURN null + +PropertyTest::__unset: + ; (lines=4, args=1, vars=1, tmps=1) + ; (before optimizer) + ; /.../PHP/35_unset_overloading/1_instance_35_unset_overloading/1_instance_35_unset_overloading.php:9-11 + ; return [] RANGE[0..0] +0000 CV0($name) = RECV 1 +0001 T1 = FETCH_OBJ_R THIS string("x") +0002 ECHO T1 +0003 RETURN null diff --git a/PHP/35_unset_overloading/1_instance_35_unset_overloading/1_instance_35_unset_overloading.json b/PHP/35_unset_overloading/1_instance_35_unset_overloading/1_instance_35_unset_overloading.json old mode 100644 new mode 100755 index 585cfe7..230b4d4 --- a/PHP/35_unset_overloading/1_instance_35_unset_overloading/1_instance_35_unset_overloading.json +++ b/PHP/35_unset_overloading/1_instance_35_unset_overloading/1_instance_35_unset_overloading.json @@ -1,37 +1,38 @@ -{ - "code": { - "path": "./1_instance_35_unset_overloading.php", - "injection_skeleton_broken": false - }, - "discovery": { - "rule": "./1_instance_35_unset_overloading.sc", - "method": "joern", - "rule_accuracy": "FP", - "notes": null - }, - "compile": { - "binary": "./1_instance_35_unset_overloading.bash", - "instruction": null, - "dependencies": null - }, - "expectation": { - "type": "xss", - "sink_file": "./1_instance_35_unset_overloading.php", - "sink_line": 19, - "source_file": "./1_instance_35_unset_overloading.php", - "source_line": 23, - "expectation": true - }, - "properties": { - "category": "S0", - "feature_vs_internal_api": "FEATURE", - "input_sanitizer": false, - "source_and_sink": false, - "negative_test_case": false - }, - "remediation": { - "notes": "", - "transformation": null, - "modeling_rule": null - } +{ + "description": "This instance shows, that `__unset` is invoked, when `unset` is called on an instance of the class.", + "code": { + "path": "./1_instance_35_unset_overloading.php", + "injection_skeleton_broken": true + }, + "discovery": { + "rule": "../35_unset_overloading.sc", + "method": "joern", + "rule_accuracy": "FP", + "notes": "../docs/discovery_notes.md" + }, + "compile": { + "binary": "./1_instance_35_unset_overloading.bash", + "instruction": null, + "dependencies": null + }, + "expectation": { + "type": "xss", + "sink_file": "./1_instance_35_unset_overloading.php", + "sink_line": 10, + "source_file": "./1_instance_35_unset_overloading.php", + "source_line": 14, + "expectation": true + }, + "properties": { + "category": "S0", + "feature_vs_internal_api": "FEATURE", + "input_sanitizer": false, + "source_and_sink": false, + "negative_test_case": false + }, + "remediation": { + "notes": "", + "transformation": null, + "modeling_rule": null + } } \ No newline at end of file diff --git a/PHP/35_unset_overloading/1_instance_35_unset_overloading/1_instance_35_unset_overloading.php b/PHP/35_unset_overloading/1_instance_35_unset_overloading/1_instance_35_unset_overloading.php old mode 100644 new mode 100755 index c93fe8c..3252f7e --- a/PHP/35_unset_overloading/1_instance_35_unset_overloading/1_instance_35_unset_overloading.php +++ b/PHP/35_unset_overloading/1_instance_35_unset_overloading/1_instance_35_unset_overloading.php @@ -1,28 +1,18 @@ -var; - } - - public function setx($b) - { - $this->x = $b; - } - - public function __unset($name) - { - echo "Unsetting " . $this->x; - } -} - -$b = $_GET["p1"]; -$obj = new PropertyTest; -$obj->var = $b; -$obj->setx($b); -// will call __unset() function, XSS vulnerability +x = $b; + } + + public function __unset($name) { + echo $this->x; // sink + } +} + +$b = $_GET["p1"]; // source +$obj = new PropertyTest; +$obj->setx($b); +// will call __unset() function, XSS vulnerability unset($obj->x); \ No newline at end of file diff --git a/PHP/35_unset_overloading/1_instance_35_unset_overloading/1_instance_35_unset_overloading.sc b/PHP/35_unset_overloading/1_instance_35_unset_overloading/1_instance_35_unset_overloading.sc deleted file mode 100644 index 99dd18a..0000000 --- a/PHP/35_unset_overloading/1_instance_35_unset_overloading/1_instance_35_unset_overloading.sc +++ /dev/null @@ -1,7 +0,0 @@ -@main def main(name : String): Unit = { - importCpg(name) - def methods4 = cpg.method.name("__unset").name.l - val x35 = (name, "35_unset_overloading_iall", cpg.call("NEW").argument.filter{x => methods4.contains(x.code.toLowerCase)}.location.toJson); - println(x35) - delete; -} \ No newline at end of file diff --git a/PHP/35_unset_overloading/2_instance_35_unset_overloading/2_instance_35_unset_overloading.bash b/PHP/35_unset_overloading/2_instance_35_unset_overloading/2_instance_35_unset_overloading.bash new file mode 100755 index 0000000..ec22a7d --- /dev/null +++ b/PHP/35_unset_overloading/2_instance_35_unset_overloading/2_instance_35_unset_overloading.bash @@ -0,0 +1,39 @@ + +$_main: + ; (lines=11, args=0, vars=2, tmps=7) + ; (before optimizer) + ; /.../PHP/35_unset_overloading/2_instance_35_unset_overloading/2_instance_35_unset_overloading.php:1-18 + ; return [] RANGE[0..0] +0000 T2 = FETCH_R (global) string("_GET") +0001 T3 = FETCH_DIM_R T2 string("p1") +0002 ASSIGN CV0($b) T3 +0003 V5 = NEW 0 string("PropertyTest") +0004 DO_FCALL +0005 ASSIGN CV1($obj) V5 +0006 INIT_METHOD_CALL 1 CV1($obj) string("setx") +0007 SEND_VAR_EX CV0($b) 1 +0008 DO_FCALL +0009 UNSET_OBJ CV1($obj) string("var") +0010 RETURN int(1) +LIVE RANGES: + 5: 0004 - 0005 (new) + +PropertyTest::setx: + ; (lines=4, args=1, vars=1, tmps=1) + ; (before optimizer) + ; /.../PHP/35_unset_overloading/2_instance_35_unset_overloading/2_instance_35_unset_overloading.php:5-7 + ; return [] RANGE[0..0] +0000 CV0($b) = RECV 1 +0001 ASSIGN_OBJ THIS string("x") +0002 OP_DATA CV0($b) +0003 RETURN null + +PropertyTest::__unset: + ; (lines=4, args=1, vars=1, tmps=1) + ; (before optimizer) + ; /.../PHP/35_unset_overloading/2_instance_35_unset_overloading/2_instance_35_unset_overloading.php:9-11 + ; return [] RANGE[0..0] +0000 CV0($name) = RECV 1 +0001 T1 = FETCH_OBJ_R THIS string("x") +0002 ECHO T1 +0003 RETURN null diff --git a/PHP/35_unset_overloading/2_instance_35_unset_overloading/2_instance_35_unset_overloading.json b/PHP/35_unset_overloading/2_instance_35_unset_overloading/2_instance_35_unset_overloading.json new file mode 100755 index 0000000..d20373b --- /dev/null +++ b/PHP/35_unset_overloading/2_instance_35_unset_overloading/2_instance_35_unset_overloading.json @@ -0,0 +1,38 @@ +{ + "description": "This instance shows, that wenn calling unset on a property of an object that does not exist it invokes calling `__unset`.", + "code": { + "path": "./2_instance_35_unset_overloading.php", + "injection_skeleton_broken": true + }, + "expectation": { + "type": "xss", + "sink_file": "./2_instance_35_unset_overloading.php", + "sink_line": 10, + "source_file": "./2_instance_35_unset_overloading.php", + "source_line": 14, + "expectation": true + }, + "compile": { + "binary": "./2_instance_35_unset_overloading.bash", + "instruction": null, + "dependencies": null + }, + "discovery": { + "rule": "../35_unset_overloading.sc", + "method": "joern", + "rule_accuracy": "FP", + "notes": "../docs/discovery_notes.md" + }, + "properties": { + "category": "S0", + "feature_vs_internal_api": "FEATURE", + "input_sanitizer": false, + "source_and_sink": false, + "negative_test_case": false + }, + "remediation": { + "notes": "", + "transformation": null, + "modeling_rule": null + } +} \ No newline at end of file diff --git a/PHP/35_unset_overloading/2_instance_35_unset_overloading/2_instance_35_unset_overloading.php b/PHP/35_unset_overloading/2_instance_35_unset_overloading/2_instance_35_unset_overloading.php new file mode 100755 index 0000000..12ee393 --- /dev/null +++ b/PHP/35_unset_overloading/2_instance_35_unset_overloading/2_instance_35_unset_overloading.php @@ -0,0 +1,18 @@ +x = $b; + } + + public function __unset($name) { + echo $this->x; // sink + } +} + +$b = $_GET["p1"]; // source +$obj = new PropertyTest; +$obj->setx($b); +// will call __unset() function because $obj->var does not exist, XSS vulnerability +unset($obj->var); \ No newline at end of file diff --git a/PHP/35_unset_overloading/35_unset_overloading.json b/PHP/35_unset_overloading/35_unset_overloading.json old mode 100644 new mode 100755 index a88eb87..151d804 --- a/PHP/35_unset_overloading/35_unset_overloading.json +++ b/PHP/35_unset_overloading/35_unset_overloading.json @@ -1,14 +1,16 @@ -{ - "name": "Unset Overloading", - "description": "", - "family": "code_pattern_php", - "tags": [ - "sast", - "php", - "php_v7.4.9" - ], - "instances": [ - "./1_instance_35_unset_overloading/1_instance_35_unset_overloading.json" - ], - "version": "v0.draft" +{ + "name": "Unset Overloading", + "description": "./docs/description.md", + "family": "code_pattern_php", + "tags": [ + "sast", + "php", + "php_v7.4.9" + ], + "instances": [ + "./1_instance_35_unset_overloading/1_instance_35_unset_overloading.json", + "./2_instance_35_unset_overloading/2_instance_35_unset_overloading.json", + "./3_instance_35_unset_overloading/3_instance_35_unset_overloading.json" + ], + "version": "v1.0" } \ No newline at end of file diff --git a/PHP/35_unset_overloading/35_unset_overloading.sc b/PHP/35_unset_overloading/35_unset_overloading.sc new file mode 100755 index 0000000..9921a91 --- /dev/null +++ b/PHP/35_unset_overloading/35_unset_overloading.sc @@ -0,0 +1,7 @@ +@main def main(name : String): Unit = { + importCpg(name) + def methodClasses = cpg.method.name("__unset").astParentFullName.l + val x35 = (name, "35_unset_overloading_iall", cpg.call("NEW").argument.filter{x => methodClasses.contains(x.code.toLowerCase)}.location.toJson); + println(x35) + delete; +} \ No newline at end of file diff --git a/PHP/35_unset_overloading/3_instance_35_unset_overloading/3_instance_35_unset_overloading.bash b/PHP/35_unset_overloading/3_instance_35_unset_overloading/3_instance_35_unset_overloading.bash new file mode 100755 index 0000000..fca13d6 --- /dev/null +++ b/PHP/35_unset_overloading/3_instance_35_unset_overloading/3_instance_35_unset_overloading.bash @@ -0,0 +1,28 @@ + +$_main: + ; (lines=10, args=0, vars=2, tmps=7) + ; (before optimizer) + ; /.../PHP/35_unset_overloading/3_instance_35_unset_overloading/3_instance_35_unset_overloading.php:1-14 + ; return [] RANGE[0..0] +0000 T2 = FETCH_R (global) string("_GET") +0001 T3 = FETCH_DIM_R T2 string("p1") +0002 ASSIGN CV0($b) T3 +0003 V5 = NEW 0 string("PropertyTest") +0004 DO_FCALL +0005 ASSIGN CV1($obj) V5 +0006 ASSIGN_OBJ CV1($obj) string("x") +0007 OP_DATA CV0($b) +0008 UNSET_OBJ CV1($obj) string("x") +0009 RETURN int(1) +LIVE RANGES: + 5: 0004 - 0005 (new) + +PropertyTest::__unset: + ; (lines=4, args=1, vars=1, tmps=1) + ; (before optimizer) + ; /.../PHP/35_unset_overloading/3_instance_35_unset_overloading/3_instance_35_unset_overloading.php:5-7 + ; return [] RANGE[0..0] +0000 CV0($name) = RECV 1 +0001 T1 = FETCH_OBJ_R THIS string("x") +0002 ECHO T1 +0003 RETURN null diff --git a/PHP/35_unset_overloading/3_instance_35_unset_overloading/3_instance_35_unset_overloading.json b/PHP/35_unset_overloading/3_instance_35_unset_overloading/3_instance_35_unset_overloading.json new file mode 100755 index 0000000..c7338e5 --- /dev/null +++ b/PHP/35_unset_overloading/3_instance_35_unset_overloading/3_instance_35_unset_overloading.json @@ -0,0 +1,38 @@ +{ + "description": "This instance is not vulnerable, because it calls `unset` on a public member of the object. This does not invoke `__unset`.", + "code": { + "path": "./3_instance_35_unset_overloading.php", + "injection_skeleton_broken": true + }, + "expectation": { + "type": "xss", + "sink_file": "./3_instance_35_unset_overloading.php", + "sink_line": 6, + "source_file": "./3_instance_35_unset_overloading.php", + "source_line": 10, + "expectation": false + }, + "compile": { + "binary": "./3_instance_35_unset_overloading.bash", + "instruction": null, + "dependencies": null + }, + "discovery": { + "rule": "../35_unset_overloading.sc", + "method": "joern", + "rule_accuracy": "FP", + "notes": "../docs/discovery_notes.md" + }, + "properties": { + "category": "S0", + "feature_vs_internal_api": "FEATURE", + "input_sanitizer": false, + "source_and_sink": false, + "negative_test_case": false + }, + "remediation": { + "notes": "", + "transformation": null, + "modeling_rule": null + } +} \ No newline at end of file diff --git a/PHP/35_unset_overloading/3_instance_35_unset_overloading/3_instance_35_unset_overloading.php b/PHP/35_unset_overloading/3_instance_35_unset_overloading/3_instance_35_unset_overloading.php new file mode 100755 index 0000000..af4a9bd --- /dev/null +++ b/PHP/35_unset_overloading/3_instance_35_unset_overloading/3_instance_35_unset_overloading.php @@ -0,0 +1,14 @@ +x; // sink + } +} + +$b = $_GET["p1"]; // source +$obj = new PropertyTest; +$obj->x = $b; +// will not call __unset() function, because x is public +unset($obj->x); \ No newline at end of file diff --git a/PHP/35_unset_overloading/Pattern Unset Overloading.md b/PHP/35_unset_overloading/Pattern Unset Overloading.md deleted file mode 100644 index 39b1ffc..0000000 --- a/PHP/35_unset_overloading/Pattern Unset Overloading.md +++ /dev/null @@ -1,9 +0,0 @@ -# Pattern: Unset Overloading - -## Category - -Objects - -## Definition - -PHP allows developers to supply a `__unset` function. This is used by `unset`. diff --git a/PHP/35_unset_overloading/README.md b/PHP/35_unset_overloading/README.md new file mode 100755 index 0000000..4a6b299 --- /dev/null +++ b/PHP/35_unset_overloading/README.md @@ -0,0 +1,386 @@ +[//]: # (This file is automatically generated. If you wish to make any changes, please use the JSON files and regenerate this file using the tpframework.) + +# Unset Overloading + +Tags: sast, php, php_v7.4.9 + +Version: v1.0 + +## Description + +In many object-oriented programming languages overloading provides the ability of having multiple methods with the same name but different quantities and types of arguments. In PHP however [Overloading](https://www.php.net/manual/en/language.oop5.overloading.php) describes the concept of dynamically creating properties and methods. This pattern targets the usage of property overloading for `__unset()`. This method is invoked when unset() is used on inaccessible (protected or private) or non-existing properties. + +## Overview + +| Instances | has discovery rule | discovery method | rule successfull | +|---------------------------|----------------------|--------------------|--------------------| +| [1 Instance](#1-instance) | yes | joern | yes | +| [2 Instance](#2-instance) | yes | joern | yes | +| [3 Instance](#3-instance) | yes | joern | yes | + +
+ + +## 1 Instance + + +This instance shows, that `__unset` is invoked, when `unset` is called on an instance of the class. + +### Code + +```PHP +x = $b; + } + + public function __unset($name) { + echo $this->x; // sink + } +} + +$b = $_GET["p1"]; // source +$obj = new PropertyTest; +$obj->setx($b); +// will call __unset() function, XSS vulnerability +unset($obj->x); +``` + +### Instance Properties + +| category | feature_vs_internal_api | input_sanitizer | negative_test_case | source_and_sink | +|------------|---------------------------|-------------------|----------------------|-------------------| +| S0 | FEATURE | no | no | no | + +
+ +More + +
+ + +### Compile + + +```bash +$_main: + ; (lines=11, args=0, vars=2, tmps=7) + ; (before optimizer) + ; /.../PHP/35_unset_overloading/1_instance_35_unset_overloading/1_instance_35_unset_overloading.php:1-18 + ; return [] RANGE[0..0] +0000 T2 = FETCH_R (global) string("_GET") +0001 T3 = FETCH_DIM_R T2 string("p1") +0002 ASSIGN CV0($b) T3 +0003 V5 = NEW 0 string("PropertyTest") +0004 DO_FCALL +0005 ASSIGN CV1($obj) V5 +0006 INIT_METHOD_CALL 1 CV1($obj) string("setx") +0007 SEND_VAR_EX CV0($b) 1 +0008 DO_FCALL +0009 UNSET_OBJ CV1($obj) string("x") +0010 RETURN int(1) +LIVE RANGES: + 5: 0004 - 0005 (new) + +PropertyTest::setx: + ; (lines=4, args=1, vars=1, tmps=1) + ; (before optimizer) + ; /.../PHP/35_unset_overloading/1_instance_35_unset_overloading/1_instance_35_unset_overloading.php:5-7 + ; return [] RANGE[0..0] +0000 CV0($b) = RECV 1 +0001 ASSIGN_OBJ THIS string("x") +0002 OP_DATA CV0($b) +0003 RETURN null + +PropertyTest::__unset: + ; (lines=4, args=1, vars=1, tmps=1) + ; (before optimizer) + ; /.../PHP/35_unset_overloading/1_instance_35_unset_overloading/1_instance_35_unset_overloading.php:9-11 + ; return [] RANGE[0..0] +0000 CV0($name) = RECV 1 +0001 T1 = FETCH_OBJ_R THIS string("x") +0002 ECHO T1 +0003 RETURN null +``` + +
+ +
+ + +### Discovery + + +The discovery rule first gets all class names, where the method `__unset` is defined. +Afterwards it collects all `NEW` calles and filters them to see which of the instanciated objects has the method `__unset` defined. +The rule would be perfect, if we could additionally check, if one of the created objects invokes the `__unset` method within its lifetime. + +```scala +def methodClasses = cpg.method.name("__unset").astParentFullName.l +val x35 = (name, "35_unset_overloading_iall", cpg.call("NEW").argument.filter{x => methodClasses.contains(x.code.toLowerCase)}.location.toJson); +``` + +| discovery method | expected accuracy | +|--------------------|---------------------| +| joern | FP | + +
+ +
+ + +### Measurement + + +| Tool | Comm_1 | Comm_2 | phpSAFE | Progpilot | RIPS | WAP | Ground Truth | +|-------------|----------|----------|-----------|-------------|--------|-------|----------------| +| 08 Jun 2021 | yes | no | no | no | no | no | yes | +| 17 May 2023 | yes | no | | | | | yes | + +
+ +
+ +
+ +
+ + +## 2 Instance + + +This instance shows, that wenn calling unset on a property of an object that does not exist it invokes calling `__unset`. + +### Code + +```PHP +x = $b; + } + + public function __unset($name) { + echo $this->x; // sink + } +} + +$b = $_GET["p1"]; // source +$obj = new PropertyTest; +$obj->setx($b); +// will call __unset() function because $obj->var does not exist, XSS vulnerability +unset($obj->var); +``` + +### Instance Properties + +| category | feature_vs_internal_api | input_sanitizer | negative_test_case | source_and_sink | +|------------|---------------------------|-------------------|----------------------|-------------------| +| S0 | FEATURE | no | no | no | + +
+ +More + +
+ + +### Compile + + +```bash +$_main: + ; (lines=11, args=0, vars=2, tmps=7) + ; (before optimizer) + ; /.../PHP/35_unset_overloading/2_instance_35_unset_overloading/2_instance_35_unset_overloading.php:1-18 + ; return [] RANGE[0..0] +0000 T2 = FETCH_R (global) string("_GET") +0001 T3 = FETCH_DIM_R T2 string("p1") +0002 ASSIGN CV0($b) T3 +0003 V5 = NEW 0 string("PropertyTest") +0004 DO_FCALL +0005 ASSIGN CV1($obj) V5 +0006 INIT_METHOD_CALL 1 CV1($obj) string("setx") +0007 SEND_VAR_EX CV0($b) 1 +0008 DO_FCALL +0009 UNSET_OBJ CV1($obj) string("var") +0010 RETURN int(1) +LIVE RANGES: + 5: 0004 - 0005 (new) + +PropertyTest::setx: + ; (lines=4, args=1, vars=1, tmps=1) + ; (before optimizer) + ; /.../PHP/35_unset_overloading/2_instance_35_unset_overloading/2_instance_35_unset_overloading.php:5-7 + ; return [] RANGE[0..0] +0000 CV0($b) = RECV 1 +0001 ASSIGN_OBJ THIS string("x") +0002 OP_DATA CV0($b) +0003 RETURN null + +PropertyTest::__unset: + ; (lines=4, args=1, vars=1, tmps=1) + ; (before optimizer) + ; /.../PHP/35_unset_overloading/2_instance_35_unset_overloading/2_instance_35_unset_overloading.php:9-11 + ; return [] RANGE[0..0] +0000 CV0($name) = RECV 1 +0001 T1 = FETCH_OBJ_R THIS string("x") +0002 ECHO T1 +0003 RETURN null +``` + +
+ +
+ + +### Discovery + + +The discovery rule first gets all class names, where the method `__unset` is defined. +Afterwards it collects all `NEW` calles and filters them to see which of the instanciated objects has the method `__unset` defined. +The rule would be perfect, if we could additionally check, if one of the created objects invokes the `__unset` method within its lifetime. + +```scala +def methodClasses = cpg.method.name("__unset").astParentFullName.l +val x35 = (name, "35_unset_overloading_iall", cpg.call("NEW").argument.filter{x => methodClasses.contains(x.code.toLowerCase)}.location.toJson); +``` + +| discovery method | expected accuracy | +|--------------------|---------------------| +| joern | FP | + +
+ +
+ + +### Measurement + + +| Tool | Comm_1 | Comm_2 | Ground Truth | +|-------------|----------|----------|----------------| +| 17 May 2023 | yes | no | yes | + +
+ +
+ +
+ +
+ + +## 3 Instance + + +This instance is not vulnerable, because it calls `unset` on a public member of the object. This does not invoke `__unset`. + +### Code + +```PHP +x; // sink + } +} + +$b = $_GET["p1"]; // source +$obj = new PropertyTest; +$obj->x = $b; +// will not call __unset() function, because x is public +unset($obj->x); +``` + +### Instance Properties + +| category | feature_vs_internal_api | input_sanitizer | negative_test_case | source_and_sink | +|------------|---------------------------|-------------------|----------------------|-------------------| +| S0 | FEATURE | no | no | no | + +
+ +More + +
+ + +### Compile + + +```bash +$_main: + ; (lines=10, args=0, vars=2, tmps=7) + ; (before optimizer) + ; /.../PHP/35_unset_overloading/3_instance_35_unset_overloading/3_instance_35_unset_overloading.php:1-14 + ; return [] RANGE[0..0] +0000 T2 = FETCH_R (global) string("_GET") +0001 T3 = FETCH_DIM_R T2 string("p1") +0002 ASSIGN CV0($b) T3 +0003 V5 = NEW 0 string("PropertyTest") +0004 DO_FCALL +0005 ASSIGN CV1($obj) V5 +0006 ASSIGN_OBJ CV1($obj) string("x") +0007 OP_DATA CV0($b) +0008 UNSET_OBJ CV1($obj) string("x") +0009 RETURN int(1) +LIVE RANGES: + 5: 0004 - 0005 (new) + +PropertyTest::__unset: + ; (lines=4, args=1, vars=1, tmps=1) + ; (before optimizer) + ; /.../PHP/35_unset_overloading/3_instance_35_unset_overloading/3_instance_35_unset_overloading.php:5-7 + ; return [] RANGE[0..0] +0000 CV0($name) = RECV 1 +0001 T1 = FETCH_OBJ_R THIS string("x") +0002 ECHO T1 +0003 RETURN null +``` + +
+ +
+ + +### Discovery + + +The discovery rule first gets all class names, where the method `__unset` is defined. +Afterwards it collects all `NEW` calles and filters them to see which of the instanciated objects has the method `__unset` defined. +The rule would be perfect, if we could additionally check, if one of the created objects invokes the `__unset` method within its lifetime. + +```scala +def methodClasses = cpg.method.name("__unset").astParentFullName.l +val x35 = (name, "35_unset_overloading_iall", cpg.call("NEW").argument.filter{x => methodClasses.contains(x.code.toLowerCase)}.location.toJson); +``` + +| discovery method | expected accuracy | +|--------------------|---------------------| +| joern | FP | + +
+ +
+ + +### Measurement + + +| Tool | Comm_1 | Comm_2 | Ground Truth | +|-------------|----------|----------|----------------| +| 17 May 2023 | no | no | no | + +
+ +
+ +
diff --git a/PHP/35_unset_overloading/docs/description.md b/PHP/35_unset_overloading/docs/description.md new file mode 100755 index 0000000..5951af5 --- /dev/null +++ b/PHP/35_unset_overloading/docs/description.md @@ -0,0 +1 @@ +In many object-oriented programming languages overloading provides the ability of having multiple methods with the same name but different quantities and types of arguments. In PHP however [Overloading](https://www.php.net/manual/en/language.oop5.overloading.php) describes the concept of dynamically creating properties and methods. This pattern targets the usage of property overloading for `__unset()`. This method is invoked when unset() is used on inaccessible (protected or private) or non-existing properties. \ No newline at end of file diff --git a/PHP/35_unset_overloading/docs/discovery_notes.md b/PHP/35_unset_overloading/docs/discovery_notes.md new file mode 100755 index 0000000..ed486b2 --- /dev/null +++ b/PHP/35_unset_overloading/docs/discovery_notes.md @@ -0,0 +1,3 @@ +The discovery rule first gets all class names, where the method `__unset` is defined. +Afterwards it collects all `NEW` calles and filters them to see which of the instanciated objects has the method `__unset` defined. +The rule would be perfect, if we could additionally check, if one of the created objects invokes the `__unset` method within its lifetime. \ No newline at end of file diff --git a/PHP/36_call_overloading/1_instance_36_call_overloading/1_instance_36_call_overloading.bash b/PHP/36_call_overloading/1_instance_36_call_overloading/1_instance_36_call_overloading.bash old mode 100644 new mode 100755 index dcd9bd0..e1a2e18 --- a/PHP/36_call_overloading/1_instance_36_call_overloading/1_instance_36_call_overloading.bash +++ b/PHP/36_call_overloading/1_instance_36_call_overloading/1_instance_36_call_overloading.bash @@ -1,45 +1,36 @@ - -$_main: ; (lines=17, args=0, vars=2, tmps=8) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/33_call_overloading/33_call_overloading.php:1-19 -L0 (12): EXT_STMT -L1 (12): T2 = FETCH_R (global) string("_GET") -L2 (12): T3 = FETCH_DIM_R T2 string("p1") -L3 (12): ASSIGN CV0($b) T3 -L4 (13): EXT_STMT -L5 (13): V5 = NEW 0 string("MethodTest") -L6 (13): DO_FCALL -L7 (13): ASSIGN CV1($obj) V5 -L8 (14): EXT_STMT -L9 (14): ASSIGN_OBJ CV1($obj) string("x") -L10 (14): OP_DATA CV0($b) -L11 (18): EXT_STMT -L12 (18): INIT_METHOD_CALL 2 CV1($obj) string("runTest") -L13 (18): SEND_VAL_EX string("arg1") 1 -L14 (18): SEND_VAR_EX CV0($b) 2 -L15 (18): DO_FCALL -L16 (19): RETURN int(1) -LIVE RANGES: - 5: L6 - L7 (new) - -MethodTest::__call: ; (lines=13, args=2, vars=3, tmps=2) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/33_call_overloading/33_call_overloading.php:4-9 -L0 (4): EXT_NOP -L1 (4): CV0($name) = RECV 1 -L2 (4): CV1($arguments) = RECV 2 -L3 (6): EXT_STMT -L4 (6): V3 = FE_RESET_R CV1($arguments) L10 -L5 (6): FE_FETCH_R V3 CV2($arg) L10 -L6 (7): EXT_STMT -L7 (7): T4 = CONCAT CV2($arg) string(" -") -L8 (7): ECHO T4 -L9 (6): JMP L5 -L10 (6): FE_FREE V3 -L11 (9): EXT_STMT -L12 (9): RETURN null -LIVE RANGES: - 3: L5 - L10 (loop) - - + +$_main: + ; (lines=12, args=0, vars=2, tmps=8) + ; (before optimizer) + ; /.../PHP/36_call_overloading/1_instance_36_call_overloading/1_instance_36_call_overloading.php:1-16 + ; return [] RANGE[0..0] +0000 T2 = FETCH_R (global) string("_GET") +0001 T3 = FETCH_DIM_R T2 string("p1") +0002 ASSIGN CV0($b) T3 +0003 V5 = NEW 0 string("MethodTest") +0004 DO_FCALL +0005 ASSIGN CV1($obj) V5 +0006 ASSIGN_OBJ CV1($obj) string("x") +0007 OP_DATA CV0($b) +0008 INIT_METHOD_CALL 1 CV1($obj) string("runTest") +0009 SEND_VAR_EX CV0($b) 1 +0010 DO_FCALL +0011 RETURN int(1) +LIVE RANGES: + 5: 0004 - 0005 (new) + +MethodTest::__call: + ; (lines=8, args=2, vars=3, tmps=1) + ; (before optimizer) + ; /.../PHP/36_call_overloading/1_instance_36_call_overloading/1_instance_36_call_overloading.php:3-7 + ; return [] RANGE[0..0] +0000 CV0($name) = RECV 1 +0001 CV1($arguments) = RECV 2 +0002 V3 = FE_RESET_R CV1($arguments) 0006 +0003 FE_FETCH_R V3 CV2($arg) 0006 +0004 ECHO CV2($arg) +0005 JMP 0003 +0006 FE_FREE V3 +0007 RETURN null +LIVE RANGES: + 3: 0003 - 0006 (loop) diff --git a/PHP/36_call_overloading/1_instance_36_call_overloading/1_instance_36_call_overloading.json b/PHP/36_call_overloading/1_instance_36_call_overloading/1_instance_36_call_overloading.json old mode 100644 new mode 100755 index 8b8c972..04776f0 --- a/PHP/36_call_overloading/1_instance_36_call_overloading/1_instance_36_call_overloading.json +++ b/PHP/36_call_overloading/1_instance_36_call_overloading/1_instance_36_call_overloading.json @@ -1,37 +1,38 @@ -{ - "code": { - "path": "./1_instance_36_call_overloading.php", - "injection_skeleton_broken": false - }, - "discovery": { - "rule": "./1_instance_36_call_overloading.sc", - "method": "joern", - "rule_accuracy": null, - "notes": null - }, - "compile": { - "binary": "./1_instance_36_call_overloading.bash", - "instruction": null, - "dependencies": null - }, - "expectation": { - "type": "xss", - "sink_file": "./1_instance_36_call_overloading.php", - "sink_line": 7, - "source_file": "./1_instance_36_call_overloading.php", - "source_line": 12, - "expectation": true - }, - "properties": { - "category": "S0", - "feature_vs_internal_api": "FEATURE", - "input_sanitizer": false, - "source_and_sink": false, - "negative_test_case": false - }, - "remediation": { - "notes": "", - "transformation": null, - "modeling_rule": null - } +{ + "description": "This instance shows how the `__call` method is invoked.", + "code": { + "path": "./1_instance_36_call_overloading.php", + "injection_skeleton_broken": true + }, + "discovery": { + "rule": "./1_instance_36_call_overloading.sc", + "method": "joern", + "rule_accuracy": "FP", + "notes": "The discovery rule first gets all class names, where the method `__call` is defined.\nAfterwards it collects all `NEW` calles and filters them to see which of the instanciated objects has the method `__call` defined.\nThe rule would be perfect, if we could additionally check, if one of the created objects invokes the `__call` method within its lifetime." + }, + "compile": { + "binary": "./1_instance_36_call_overloading.bash", + "instruction": null, + "dependencies": null + }, + "expectation": { + "type": "xss", + "sink_file": "./1_instance_36_call_overloading.php", + "sink_line": 5, + "source_file": "./1_instance_36_call_overloading.php", + "source_line": 10, + "expectation": true + }, + "properties": { + "category": "S0", + "feature_vs_internal_api": "FEATURE", + "input_sanitizer": false, + "source_and_sink": false, + "negative_test_case": false + }, + "remediation": { + "notes": "", + "transformation": null, + "modeling_rule": null + } } \ No newline at end of file diff --git a/PHP/36_call_overloading/1_instance_36_call_overloading/1_instance_36_call_overloading.php b/PHP/36_call_overloading/1_instance_36_call_overloading/1_instance_36_call_overloading.php old mode 100644 new mode 100755 index c87bc18..54e81f4 --- a/PHP/36_call_overloading/1_instance_36_call_overloading/1_instance_36_call_overloading.php +++ b/PHP/36_call_overloading/1_instance_36_call_overloading/1_instance_36_call_overloading.php @@ -1,18 +1,15 @@ -x = $b; -// Will call the __call() function -// and print the argument $b -// XSS vulnerability -$obj->runTest('arg1',$b); +x = $b; +// Will call the __call() function and print the argument $b +// XSS vulnerability +$obj->runTest($b); diff --git a/PHP/36_call_overloading/1_instance_36_call_overloading/1_instance_36_call_overloading.sc b/PHP/36_call_overloading/1_instance_36_call_overloading/1_instance_36_call_overloading.sc old mode 100644 new mode 100755 index 5f52962..9907b18 --- a/PHP/36_call_overloading/1_instance_36_call_overloading/1_instance_36_call_overloading.sc +++ b/PHP/36_call_overloading/1_instance_36_call_overloading/1_instance_36_call_overloading.sc @@ -1,7 +1,8 @@ -@main def main(name : String): Unit = { - importCpg(name) - def methods5 = cpg.method.name("__call").name.l - val x36 = (name, "36_call_overloading_iall", cpg.call("NEW").argument.filter{x => methods5.contains(x.code.toLowerCase)}.location.toJson); - println(x36) - delete; +@main def main(name : String): Unit = { + importCpg(name) + // Get all classes (lowercase) where `__call` is defined + def methodClasses = cpg.method.name("__call").astParentFullName.l + val x36 = (name, "36_call_overloading_iall", cpg.call("NEW").argument.filter{x => methodClasses.contains(x.code.toLowerCase)}.location.toJson); + println(x36) + delete; } \ No newline at end of file diff --git a/PHP/36_call_overloading/36_call_overloading.json b/PHP/36_call_overloading/36_call_overloading.json old mode 100644 new mode 100755 index f98128c..6b956c5 --- a/PHP/36_call_overloading/36_call_overloading.json +++ b/PHP/36_call_overloading/36_call_overloading.json @@ -1,14 +1,14 @@ -{ - "name": "Call Overloading", - "description": "", - "family": "code_pattern_php", - "tags": [ - "sast", - "php", - "php_v7.4.9" - ], - "instances": [ - "./1_instance_36_call_overloading/1_instance_36_call_overloading.json" - ], - "version": "v1.0" +{ + "name": "Call Overloading", + "description": "./docs/description.md", + "family": "code_pattern_php", + "tags": [ + "sast", + "php", + "php_v7.4.9" + ], + "instances": [ + "./1_instance_36_call_overloading/1_instance_36_call_overloading.json" + ], + "version": "v1.0" } \ No newline at end of file diff --git a/PHP/36_call_overloading/Pattern Call Overloading.md b/PHP/36_call_overloading/Pattern Call Overloading.md deleted file mode 100644 index 1bd97b2..0000000 --- a/PHP/36_call_overloading/Pattern Call Overloading.md +++ /dev/null @@ -1,111 +0,0 @@ -# Pattern: Call Overloading - -## Category - -Objects - -## Definition - -## Instances - -### Instance 1 - -- CATEGORY: S0 -- FEATURE vs INTERNAL API: FEATURE -- INPUT SANITIZERS: NO -- SOURCES AND SINKS: NO -- NEGATIVE TEST CASES: NO -- CODE: - -```php -x = $b; -$obj = new MethodTest; -// Will call the __call() function -// and print the argument $b -// XSS vulnerability -$obj->runTest('arg1',$b); -``` - -- MEASUREMENT: - -| Tool | RIPS | phpSAFE | WAP | Progpilot | Comm_1 | Comm_2 | Correct | -| ------------- | ---- | ------- | ---- | --------- | ------- | --------- | ------- | -| Vulnerability | NO | NO | NO | NO | NO | NO | YES | -Measurements Date: 8 June 2021 - -- OPCODE: - -```bash -$_main: ; (lines=17, args=0, vars=2, tmps=8) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/33_call_overloading/33_call_overloading.php:1-19 -L0 (12): EXT_STMT -L1 (12): T2 = FETCH_R (global) string("_GET") -L2 (12): T3 = FETCH_DIM_R T2 string("p1") -L3 (12): ASSIGN CV0($b) T3 -L4 (13): EXT_STMT -L5 (13): V5 = NEW 0 string("MethodTest") -L6 (13): DO_FCALL -L7 (13): ASSIGN CV1($obj) V5 -L8 (14): EXT_STMT -L9 (14): ASSIGN_OBJ CV1($obj) string("x") -L10 (14): OP_DATA CV0($b) -L11 (18): EXT_STMT -L12 (18): INIT_METHOD_CALL 2 CV1($obj) string("runTest") -L13 (18): SEND_VAL_EX string("arg1") 1 -L14 (18): SEND_VAR_EX CV0($b) 2 -L15 (18): DO_FCALL -L16 (19): RETURN int(1) -LIVE RANGES: - 5: L6 - L7 (new) - -MethodTest::__call: ; (lines=13, args=2, vars=3, tmps=2) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/33_call_overloading/33_call_overloading.php:4-9 -L0 (4): EXT_NOP -L1 (4): CV0($name) = RECV 1 -L2 (4): CV1($arguments) = RECV 2 -L3 (6): EXT_STMT -L4 (6): V3 = FE_RESET_R CV1($arguments) L10 -L5 (6): FE_FETCH_R V3 CV2($arg) L10 -L6 (7): EXT_STMT -L7 (7): T4 = CONCAT CV2($arg) string(" -") -L8 (7): ECHO T4 -L9 (6): JMP L5 -L10 (6): FE_FREE V3 -L11 (9): EXT_STMT -L12 (9): RETURN null -LIVE RANGES: - 3: L5 - L10 (loop) -``` - -- DISCOVERY: - -```bash -def methods1 = cpg.typeDecl.filter{x => x.method.name.l.contains("__call")}.name.l -cpg.call("NEW").argument.filter{x => methods1.contains(x.code.toLowerCase)}.size; -``` - -- PRECONDITIONS: - 1. - -- TRANSFORMATION: - -``` - -``` - - diff --git a/PHP/36_call_overloading/README.md b/PHP/36_call_overloading/README.md new file mode 100755 index 0000000..5d0d6e0 --- /dev/null +++ b/PHP/36_call_overloading/README.md @@ -0,0 +1,134 @@ +[//]: # (This file is automatically generated. If you wish to make any changes, please use the JSON files and regenerate this file using the tpframework.) + +# Call Overloading + +Tags: sast, php, php_v7.4.9 + +Version: v1.0 + +## Description + +In many object-oriented programming languages overloading provides the ability of having multiple methods with the same name but different quantities and types of arguments. In PHP however [Overloading](https://www.php.net/manual/en/language.oop5.overloading.php) describes the concept of dynamically creating properties and methods. This pattern targets the usage of method overloading for `__call()`. This method is triggered when invoking inaccessible methods in an object context. + +## Overview + +| Instances | has discovery rule | discovery method | rule successfull | +|---------------------------|----------------------|--------------------|--------------------| +| [1 Instance](#1-instance) | yes | joern | yes | + +## 1 Instance + +This instance shows how the `__call` method is invoked. + +### Code + +```PHP +x = $b; +// Will call the __call() function and print the argument $b +// XSS vulnerability +$obj->runTest($b); +``` + +### Instance Properties + +| category | feature_vs_internal_api | input_sanitizer | negative_test_case | source_and_sink | +|------------|---------------------------|-------------------|----------------------|-------------------| +| S0 | FEATURE | no | no | no | + +
+ +More + +
+ + +### Compile + + +```bash +$_main: + ; (lines=12, args=0, vars=2, tmps=8) + ; (before optimizer) + ; /.../PHP/36_call_overloading/1_instance_36_call_overloading/1_instance_36_call_overloading.php:1-16 + ; return [] RANGE[0..0] +0000 T2 = FETCH_R (global) string("_GET") +0001 T3 = FETCH_DIM_R T2 string("p1") +0002 ASSIGN CV0($b) T3 +0003 V5 = NEW 0 string("MethodTest") +0004 DO_FCALL +0005 ASSIGN CV1($obj) V5 +0006 ASSIGN_OBJ CV1($obj) string("x") +0007 OP_DATA CV0($b) +0008 INIT_METHOD_CALL 1 CV1($obj) string("runTest") +0009 SEND_VAR_EX CV0($b) 1 +0010 DO_FCALL +0011 RETURN int(1) +LIVE RANGES: + 5: 0004 - 0005 (new) + +MethodTest::__call: + ; (lines=8, args=2, vars=3, tmps=1) + ; (before optimizer) + ; /.../PHP/36_call_overloading/1_instance_36_call_overloading/1_instance_36_call_overloading.php:3-7 + ; return [] RANGE[0..0] +0000 CV0($name) = RECV 1 +0001 CV1($arguments) = RECV 2 +0002 V3 = FE_RESET_R CV1($arguments) 0006 +0003 FE_FETCH_R V3 CV2($arg) 0006 +0004 ECHO CV2($arg) +0005 JMP 0003 +0006 FE_FREE V3 +0007 RETURN null +LIVE RANGES: + 3: 0003 - 0006 (loop) +``` + +
+ +
+ + +### Discovery + + +The discovery rule first gets all class names, where the method `__call` is defined. +Afterwards it collects all `NEW` calles and filters them to see which of the instanciated objects has the method `__call` defined. +The rule would be perfect, if we could additionally check, if one of the created objects invokes the `__call` method within its lifetime. + +```scala +// Get all classes (lowercase) where `__call` is defined +def methodClasses = cpg.method.name("__call").astParentFullName.l +val x36 = (name, "36_call_overloading_iall", cpg.call("NEW").argument.filter{x => methodClasses.contains(x.code.toLowerCase)}.location.toJson); +``` + +| discovery method | expected accuracy | +|--------------------|---------------------| +| joern | FP | + +
+ +
+ + +### Measurement + + +| Tool | Comm_1 | Comm_2 | phpSAFE | Progpilot | RIPS | WAP | Ground Truth | +|-------------|----------|----------|-----------|-------------|--------|-------|----------------| +| 08 Jun 2021 | no | no | no | no | no | no | yes | +| 17 May 2023 | yes | no | | | | | yes | + +
+ +
diff --git a/PHP/36_call_overloading/docs/description.md b/PHP/36_call_overloading/docs/description.md new file mode 100755 index 0000000..432598a --- /dev/null +++ b/PHP/36_call_overloading/docs/description.md @@ -0,0 +1 @@ +In many object-oriented programming languages overloading provides the ability of having multiple methods with the same name but different quantities and types of arguments. In PHP however [Overloading](https://www.php.net/manual/en/language.oop5.overloading.php) describes the concept of dynamically creating properties and methods. This pattern targets the usage of method overloading for `__call()`. This method is triggered when invoking inaccessible methods in an object context. \ No newline at end of file diff --git a/PHP/37_callstatic_overloading/1_instance_37_callstatic_overloading/1_instance_37_callstatic_overloading.bash b/PHP/37_callstatic_overloading/1_instance_37_callstatic_overloading/1_instance_37_callstatic_overloading.bash old mode 100644 new mode 100755 index 7c07cf4..0d136f1 --- a/PHP/37_callstatic_overloading/1_instance_37_callstatic_overloading/1_instance_37_callstatic_overloading.bash +++ b/PHP/37_callstatic_overloading/1_instance_37_callstatic_overloading/1_instance_37_callstatic_overloading.bash @@ -1,43 +1,36 @@ - -$_main: ; (lines=17, args=0, vars=2, tmps=8) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/124_callstatic_overloading/124_callstatic_overloading.php:1-19 -L0 (12): EXT_STMT -L1 (12): T2 = FETCH_R (global) string("_GET") -L2 (12): T3 = FETCH_DIM_R T2 string("p1") -L3 (12): ASSIGN CV0($b) T3 -L4 (13): EXT_STMT -L5 (13): V5 = NEW 0 string("MethodTest") -L6 (13): DO_FCALL -L7 (13): ASSIGN CV1($obj) V5 -L8 (14): EXT_STMT -L9 (14): ASSIGN_OBJ CV1($obj) string("x") -L10 (14): OP_DATA CV0($b) -L11 (18): EXT_STMT -L12 (18): INIT_STATIC_METHOD_CALL 2 string("MethodTest") string("runTest") -L13 (18): SEND_VAL_EX string("arg1") 1 -L14 (18): SEND_VAR_EX CV0($b) 2 -L15 (18): DO_FCALL -L16 (19): RETURN int(1) -LIVE RANGES: - 5: L6 - L7 (new) - -MethodTest::__callStatic: ; (lines=13, args=2, vars=3, tmps=2) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/124_callstatic_overloading/124_callstatic_overloading.php:4-9 -L0 (4): EXT_NOP -L1 (4): CV0($name) = RECV 1 -L2 (4): CV1($arguments) = RECV 2 -L3 (6): EXT_STMT -L4 (6): V3 = FE_RESET_R CV1($arguments) L10 -L5 (6): FE_FETCH_R V3 CV2($arg) L10 -L6 (7): EXT_STMT -L7 (7): T4 = CONCAT CV2($arg) string(" -") -L8 (7): ECHO T4 -L9 (6): JMP L5 -L10 (6): FE_FREE V3 -L11 (9): EXT_STMT -L12 (9): RETURN null -LIVE RANGES: - 3: L5 - L10 (loop) + +$_main: + ; (lines=12, args=0, vars=2, tmps=8) + ; (before optimizer) + ; /.../PHP/37_callstatic_overloading/1_instance_37_callstatic_overloading/1_instance_37_callstatic_overloading.php:1-17 + ; return [] RANGE[0..0] +0000 T2 = FETCH_R (global) string("_GET") +0001 T3 = FETCH_DIM_R T2 string("p1") +0002 ASSIGN CV0($b) T3 +0003 V5 = NEW 0 string("MethodTest") +0004 DO_FCALL +0005 ASSIGN CV1($obj) V5 +0006 ASSIGN_OBJ CV1($obj) string("x") +0007 OP_DATA CV0($b) +0008 INIT_STATIC_METHOD_CALL 1 string("MethodTest") string("runTest") +0009 SEND_VAR_EX CV0($b) 1 +0010 DO_FCALL +0011 RETURN int(1) +LIVE RANGES: + 5: 0004 - 0005 (new) + +MethodTest::__callStatic: + ; (lines=8, args=2, vars=3, tmps=1) + ; (before optimizer) + ; /.../PHP/37_callstatic_overloading/1_instance_37_callstatic_overloading/1_instance_37_callstatic_overloading.php:3-7 + ; return [] RANGE[0..0] +0000 CV0($name) = RECV 1 +0001 CV1($arguments) = RECV 2 +0002 V3 = FE_RESET_R CV1($arguments) 0006 +0003 FE_FETCH_R V3 CV2($arg) 0006 +0004 ECHO CV2($arg) +0005 JMP 0003 +0006 FE_FREE V3 +0007 RETURN null +LIVE RANGES: + 3: 0003 - 0006 (loop) diff --git a/PHP/37_callstatic_overloading/1_instance_37_callstatic_overloading/1_instance_37_callstatic_overloading.json b/PHP/37_callstatic_overloading/1_instance_37_callstatic_overloading/1_instance_37_callstatic_overloading.json old mode 100644 new mode 100755 index 5d9ee58..7997c29 --- a/PHP/37_callstatic_overloading/1_instance_37_callstatic_overloading/1_instance_37_callstatic_overloading.json +++ b/PHP/37_callstatic_overloading/1_instance_37_callstatic_overloading/1_instance_37_callstatic_overloading.json @@ -1,37 +1,38 @@ -{ - "code": { - "path": "./1_instance_37_callstatic_overloading.php", - "injection_skeleton_broken": false - }, - "discovery": { - "rule": "./1_instance_37_callstatic_overloading.sc", - "method": "joern", - "rule_accuracy": "FP", - "notes": null - }, - "compile": { - "binary": "./1_instance_37_callstatic_overloading.bash", - "instruction": null, - "dependencies": null - }, - "expectation": { - "type": "xss", - "sink_file": "./1_instance_37_callstatic_overloading.php", - "sink_line": 7, - "source_file": "./1_instance_37_callstatic_overloading.php", - "source_line": 12, - "expectation": true - }, - "properties": { - "category": "S0", - "feature_vs_internal_api": "FEATURE", - "input_sanitizer": false, - "source_and_sink": false, - "negative_test_case": false - }, - "remediation": { - "notes": "", - "transformation": null, - "modeling_rule": null - } +{ + "description": "This instance shows, how `__callstatic` is used, when calling a static function on a class.", + "code": { + "path": "./1_instance_37_callstatic_overloading.php", + "injection_skeleton_broken": false + }, + "discovery": { + "rule": "./1_instance_37_callstatic_overloading.sc", + "method": "joern", + "rule_accuracy": "FP", + "notes": "The discovery rule first gets all class names, where the method `__callstatic` is defined.\nAfterwards it filters all `INIT_STATIC_METHOD_CALL` if an argument contains any of the classes where `__callstatic` was defined." + }, + "compile": { + "binary": "./1_instance_37_callstatic_overloading.bash", + "instruction": null, + "dependencies": null + }, + "expectation": { + "type": "xss", + "sink_file": "./1_instance_37_callstatic_overloading.php", + "sink_line": 5, + "source_file": "./1_instance_37_callstatic_overloading.php", + "source_line": 10, + "expectation": true + }, + "properties": { + "category": "S0", + "feature_vs_internal_api": "FEATURE", + "input_sanitizer": false, + "source_and_sink": false, + "negative_test_case": false + }, + "remediation": { + "notes": "", + "transformation": null, + "modeling_rule": null + } } \ No newline at end of file diff --git a/PHP/37_callstatic_overloading/1_instance_37_callstatic_overloading/1_instance_37_callstatic_overloading.php b/PHP/37_callstatic_overloading/1_instance_37_callstatic_overloading/1_instance_37_callstatic_overloading.php old mode 100644 new mode 100755 index 642a027..58a4f6b --- a/PHP/37_callstatic_overloading/1_instance_37_callstatic_overloading/1_instance_37_callstatic_overloading.php +++ b/PHP/37_callstatic_overloading/1_instance_37_callstatic_overloading/1_instance_37_callstatic_overloading.php @@ -1,18 +1,16 @@ -x = $b; -// Will call the __call() function -// and print the argument $b -// XSS vulnerability -MethodTest::runTest('arg1',$b); +x = $b; +// Will call the __call() function +// and print the argument $b +// XSS vulnerability +MethodTest::runTest($b); diff --git a/PHP/37_callstatic_overloading/1_instance_37_callstatic_overloading/1_instance_37_callstatic_overloading.sc b/PHP/37_callstatic_overloading/1_instance_37_callstatic_overloading/1_instance_37_callstatic_overloading.sc old mode 100644 new mode 100755 index 06127bc..5911044 --- a/PHP/37_callstatic_overloading/1_instance_37_callstatic_overloading/1_instance_37_callstatic_overloading.sc +++ b/PHP/37_callstatic_overloading/1_instance_37_callstatic_overloading/1_instance_37_callstatic_overloading.sc @@ -1,7 +1,7 @@ -@main def main(name : String): Unit = { - importCpg(name) - def methods6 = cpg.method.name("__callstatic").name.l - val x37 = (name, "37_callstatic_overloading_iall", cpg.call("INIT_STATIC_METHOD_CALL").argument.filter{x => methods6.contains(x.code.toLowerCase)}.location.toJson); - println(x37) - delete; +@main def main(name : String): Unit = { + importCpg(name) + def methodClasses = cpg.method.name("__callstatic").astParentFullName.l + val x37 = (name, "37_callstatic_overloading_iall", cpg.call("INIT_STATIC_METHOD_CALL").argument.filter{x => methodClasses.contains(x.code.toLowerCase)}.location.toJson); + println(x37) + delete; } \ No newline at end of file diff --git a/PHP/37_callstatic_overloading/37_callstatic_overloading.json b/PHP/37_callstatic_overloading/37_callstatic_overloading.json old mode 100644 new mode 100755 index 3e8f7d4..a9faa35 --- a/PHP/37_callstatic_overloading/37_callstatic_overloading.json +++ b/PHP/37_callstatic_overloading/37_callstatic_overloading.json @@ -1,14 +1,14 @@ -{ - "name": "Callstatic Overloading", - "description": "", - "family": "code_pattern_php", - "tags": [ - "sast", - "php", - "php_v7.4.9" - ], - "instances": [ - "./1_instance_37_callstatic_overloading/1_instance_37_callstatic_overloading.json" - ], - "version": "v1.0" +{ + "name": "Callstatic Overloading", + "description": "./docs/description.md", + "family": "code_pattern_php", + "tags": [ + "sast", + "php", + "php_v7.4.9" + ], + "instances": [ + "./1_instance_37_callstatic_overloading/1_instance_37_callstatic_overloading.json" + ], + "version": "v1.0" } \ No newline at end of file diff --git a/PHP/37_callstatic_overloading/Pattern Callstatic Overloading.md b/PHP/37_callstatic_overloading/Pattern Callstatic Overloading.md deleted file mode 100644 index 7a166f5..0000000 --- a/PHP/37_callstatic_overloading/Pattern Callstatic Overloading.md +++ /dev/null @@ -1,112 +0,0 @@ -# Pattern: Callstatic Overloading - -## Category - -Objects - -## Definition - -https://www.php.net/manual/en/language.oop5.overloading.php#object.callstatic - -## Instances - -### Instance 1 - -- CATEGORY: S0 -- FEATURE vs INTERNAL API: FEATURE -- INPUT SANITIZERS: NO -- SOURCES AND SINKS: NO -- NEGATIVE TEST CASES: NO -- CODE: - -```php -x = $b; -// Will call the __call() function -// and print the argument $b -// XSS vulnerability -MethodTest::runTest('arg1',$b); -``` - -- MEASUREMENT: - -| Tool | RIPS | phpSAFE | WAP | Progpilot | Comm_1 | Comm_2 | Correct | -| ------------- | ---- | ------- | ---- | --------- | ------- | --------- | ------- | -| Vulnerability | NO | NO | NO | NO | NO | NO | YES | -Measurements Date: 8 June 2021 - -- OPCODE: - -```bash -$_main: ; (lines=17, args=0, vars=2, tmps=8) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/124_callstatic_overloading/124_callstatic_overloading.php:1-19 -L0 (12): EXT_STMT -L1 (12): T2 = FETCH_R (global) string("_GET") -L2 (12): T3 = FETCH_DIM_R T2 string("p1") -L3 (12): ASSIGN CV0($b) T3 -L4 (13): EXT_STMT -L5 (13): V5 = NEW 0 string("MethodTest") -L6 (13): DO_FCALL -L7 (13): ASSIGN CV1($obj) V5 -L8 (14): EXT_STMT -L9 (14): ASSIGN_OBJ CV1($obj) string("x") -L10 (14): OP_DATA CV0($b) -L11 (18): EXT_STMT -L12 (18): INIT_STATIC_METHOD_CALL 2 string("MethodTest") string("runTest") -L13 (18): SEND_VAL_EX string("arg1") 1 -L14 (18): SEND_VAR_EX CV0($b) 2 -L15 (18): DO_FCALL -L16 (19): RETURN int(1) -LIVE RANGES: - 5: L6 - L7 (new) - -MethodTest::__callStatic: ; (lines=13, args=2, vars=3, tmps=2) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/124_callstatic_overloading/124_callstatic_overloading.php:4-9 -L0 (4): EXT_NOP -L1 (4): CV0($name) = RECV 1 -L2 (4): CV1($arguments) = RECV 2 -L3 (6): EXT_STMT -L4 (6): V3 = FE_RESET_R CV1($arguments) L10 -L5 (6): FE_FETCH_R V3 CV2($arg) L10 -L6 (7): EXT_STMT -L7 (7): T4 = CONCAT CV2($arg) string(" -") -L8 (7): ECHO T4 -L9 (6): JMP L5 -L10 (6): FE_FREE V3 -L11 (9): EXT_STMT -L12 (9): RETURN null -LIVE RANGES: - 3: L5 - L10 (loop) -``` - -- DISCOVERY: - -```bash -def methods1 = cpg.typeDecl.filter{x => x.method.name.l.contains("__callstatic")}.name.l -cpg.call("NEW").argument.filter{x => methods1.contains(x.code.toLowerCase)}.size; -``` - -- PRECONDITIONS: - 1. - -- TRANSFORMATION: - -``` - -``` - diff --git a/PHP/37_callstatic_overloading/README.md b/PHP/37_callstatic_overloading/README.md new file mode 100755 index 0000000..db4b873 --- /dev/null +++ b/PHP/37_callstatic_overloading/README.md @@ -0,0 +1,133 @@ +[//]: # (This file is automatically generated. If you wish to make any changes, please use the JSON files and regenerate this file using the tpframework.) + +# Callstatic Overloading + +Tags: sast, php, php_v7.4.9 + +Version: v1.0 + +## Description + +In many object-oriented programming languages overloading provides the ability of having multiple methods with the same name but different quantities and types of arguments. In PHP however [Overloading](https://www.php.net/manual/en/language.oop5.overloading.php) describes the concept of dynamically creating properties and methods. This pattern targets the usage of method overloading for `__callStatic()`. This method is triggered when invoking inaccessible methods in a static context. + +## Overview + +| Instances | has discovery rule | discovery method | rule successfull | +|---------------------------|----------------------|--------------------|--------------------| +| [1 Instance](#1-instance) | yes | joern | yes | + +## 1 Instance + +This instance shows, how `__callstatic` is used, when calling a static function on a class. + +### Code + +```PHP +x = $b; +// Will call the __call() function +// and print the argument $b +// XSS vulnerability +MethodTest::runTest($b); +``` + +### Instance Properties + +| category | feature_vs_internal_api | input_sanitizer | negative_test_case | source_and_sink | +|------------|---------------------------|-------------------|----------------------|-------------------| +| S0 | FEATURE | no | no | no | + +
+ +More + +
+ + +### Compile + + +```bash +$_main: + ; (lines=12, args=0, vars=2, tmps=8) + ; (before optimizer) + ; /.../PHP/37_callstatic_overloading/1_instance_37_callstatic_overloading/1_instance_37_callstatic_overloading.php:1-17 + ; return [] RANGE[0..0] +0000 T2 = FETCH_R (global) string("_GET") +0001 T3 = FETCH_DIM_R T2 string("p1") +0002 ASSIGN CV0($b) T3 +0003 V5 = NEW 0 string("MethodTest") +0004 DO_FCALL +0005 ASSIGN CV1($obj) V5 +0006 ASSIGN_OBJ CV1($obj) string("x") +0007 OP_DATA CV0($b) +0008 INIT_STATIC_METHOD_CALL 1 string("MethodTest") string("runTest") +0009 SEND_VAR_EX CV0($b) 1 +0010 DO_FCALL +0011 RETURN int(1) +LIVE RANGES: + 5: 0004 - 0005 (new) + +MethodTest::__callStatic: + ; (lines=8, args=2, vars=3, tmps=1) + ; (before optimizer) + ; /.../PHP/37_callstatic_overloading/1_instance_37_callstatic_overloading/1_instance_37_callstatic_overloading.php:3-7 + ; return [] RANGE[0..0] +0000 CV0($name) = RECV 1 +0001 CV1($arguments) = RECV 2 +0002 V3 = FE_RESET_R CV1($arguments) 0006 +0003 FE_FETCH_R V3 CV2($arg) 0006 +0004 ECHO CV2($arg) +0005 JMP 0003 +0006 FE_FREE V3 +0007 RETURN null +LIVE RANGES: + 3: 0003 - 0006 (loop) +``` + +
+ +
+ + +### Discovery + + +The discovery rule first gets all class names, where the method `__callstatic` is defined. +Afterwards it filters all `INIT_STATIC_METHOD_CALL` if an argument contains any of the classes where `__callstatic` was defined. + +```scala +def methodClasses = cpg.method.name("__callstatic").astParentFullName.l +val x37 = (name, "37_callstatic_overloading_iall", cpg.call("INIT_STATIC_METHOD_CALL").argument.filter{x => methodClasses.contains(x.code.toLowerCase)}.location.toJson); +``` + +| discovery method | expected accuracy | +|--------------------|---------------------| +| joern | FP | + +
+ +
+ + +### Measurement + + +| Tool | Comm_1 | Comm_2 | phpSAFE | Progpilot | RIPS | WAP | Ground Truth | +|-------------|----------|----------|-----------|-------------|--------|-------|----------------| +| 08 Jun 2021 | no | no | no | no | no | no | yes | +| 17 May 2023 | no | no | | | | | yes | + +
+ +
diff --git a/PHP/37_callstatic_overloading/docs/description.md b/PHP/37_callstatic_overloading/docs/description.md new file mode 100755 index 0000000..2980dfc --- /dev/null +++ b/PHP/37_callstatic_overloading/docs/description.md @@ -0,0 +1 @@ +In many object-oriented programming languages overloading provides the ability of having multiple methods with the same name but different quantities and types of arguments. In PHP however [Overloading](https://www.php.net/manual/en/language.oop5.overloading.php) describes the concept of dynamically creating properties and methods. This pattern targets the usage of method overloading for `__callStatic()`. This method is triggered when invoking inaccessible methods in a static context. \ No newline at end of file diff --git a/PHP/38_invoke/1_instance_38_invoke/1_instance_38_invoke.bash b/PHP/38_invoke/1_instance_38_invoke/1_instance_38_invoke.bash old mode 100644 new mode 100755 index 0dc9280..a8ec950 --- a/PHP/38_invoke/1_instance_38_invoke/1_instance_38_invoke.bash +++ b/PHP/38_invoke/1_instance_38_invoke/1_instance_38_invoke.bash @@ -1,35 +1,29 @@ - -$_main: ; (lines=19, args=0, vars=3, tmps=9) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/35_invoke/35_invoke.php:1-13 -L0 (9): EXT_STMT -L1 (9): V3 = NEW 0 string("CallableClass") -L2 (9): DO_FCALL -L3 (9): ASSIGN CV0($obj) V3 -L4 (10): EXT_STMT -L5 (10): INIT_FCALL 2 112 string("fopen") -L6 (10): SEND_VAL string("php://stdin") 1 -L7 (10): SEND_VAL string("r") 2 -L8 (10): V6 = DO_FCALL -L9 (10): ASSIGN CV1($_fp) V6 -L10 (11): EXT_STMT -L11 (11): T8 = FETCH_R (global) string("_GET") -L12 (11): T9 = FETCH_DIM_R T8 string("p1") -L13 (11): ASSIGN CV2($b) T9 -L14 (13): EXT_STMT -L15 (13): INIT_DYNAMIC_CALL 1 CV0($obj) -L16 (13): SEND_VAR_EX CV2($b) 1 -L17 (13): DO_FCALL -L18 (13): RETURN int(1) -LIVE RANGES: - 3: L2 - L3 (new) - -CallableClass::__invoke: ; (lines=6, args=1, vars=1, tmps=0) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/35_invoke/35_invoke.php:4-7 -L0 (4): EXT_NOP -L1 (4): CV0($x) = RECV 1 -L2 (6): EXT_STMT -L3 (6): ECHO CV0($x) -L4 (7): EXT_STMT -L5 (7): RETURN null + +$_main: + ; (lines=12, args=0, vars=3, tmps=8) + ; (before optimizer) + ; /.../PHP/38_invoke/1_instance_38_invoke/1_instance_38_invoke.php:1-10 + ; return [] RANGE[0..0] +0000 V3 = NEW 0 string("CallableClass") +0001 DO_FCALL +0002 ASSIGN CV0($obj) V3 +0003 T6 = FETCH_R (global) string("_GET") +0004 T7 = FETCH_DIM_R T6 string("p1") +0005 ASSIGN CV1($b) T7 +0006 INIT_DYNAMIC_CALL 1 CV0($obj) +0007 SEND_VAR_EX CV1($b) 1 +0008 V9 = DO_FCALL +0009 ASSIGN CV2($a) V9 +0010 ECHO CV2($a) +0011 RETURN int(1) +LIVE RANGES: + 3: 0001 - 0002 (new) + +CallableClass::__invoke: + ; (lines=3, args=1, vars=1, tmps=0) + ; (before optimizer) + ; /.../PHP/38_invoke/1_instance_38_invoke/1_instance_38_invoke.php:3-5 + ; return [] RANGE[0..0] +0000 CV0($x) = RECV 1 +0001 RETURN CV0($x) +0002 RETURN null diff --git a/PHP/38_invoke/1_instance_38_invoke/1_instance_38_invoke.json b/PHP/38_invoke/1_instance_38_invoke/1_instance_38_invoke.json old mode 100644 new mode 100755 index ed7fde9..ba36698 --- a/PHP/38_invoke/1_instance_38_invoke/1_instance_38_invoke.json +++ b/PHP/38_invoke/1_instance_38_invoke/1_instance_38_invoke.json @@ -1,37 +1,38 @@ -{ - "code": { - "path": "./1_instance_38_invoke.php", - "injection_skeleton_broken": false - }, - "discovery": { - "rule": "./1_instance_38_invoke.sc", - "method": "joern", - "rule_accuracy": null, - "notes": null - }, - "compile": { - "binary": "./1_instance_38_invoke.bash", - "instruction": null, - "dependencies": null - }, - "expectation": { - "type": "xss", - "sink_file": "./1_instance_38_invoke.php", - "sink_line": 6, - "source_file": "./1_instance_38_invoke.php", - "source_line": 11, - "expectation": true - }, - "properties": { - "category": "S0", - "feature_vs_internal_api": "FEATURE", - "input_sanitizer": false, - "source_and_sink": false, - "negative_test_case": false - }, - "remediation": { - "notes": "", - "transformation": null, - "modeling_rule": null - } +{ + "description": "This instance shows, that when you have a callable object and you call the object, `__invoke` will be called.", + "code": { + "path": "./1_instance_38_invoke.php", + "injection_skeleton_broken": false + }, + "discovery": { + "rule": "./1_instance_38_invoke.sc", + "method": "joern", + "rule_accuracy": "FP", + "notes": "The discovery rule first gets all class names, where the method `__invoke` is defined.\nAfterwards it collects all `NEW` calles and filters them to see which of the instanciated objects has the method `__invoke` defined.\nThe rule would be perfect, if we could additionally check, if one of the created objects invokes the `__invoke` method within its lifetime." + }, + "compile": { + "binary": "./1_instance_38_invoke.bash", + "instruction": null, + "dependencies": null + }, + "expectation": { + "type": "xss", + "sink_file": "./1_instance_38_invoke.php", + "sink_line": 10, + "source_file": "./1_instance_38_invoke.php", + "source_line": 8, + "expectation": true + }, + "properties": { + "category": "S0", + "feature_vs_internal_api": "FEATURE", + "input_sanitizer": false, + "source_and_sink": false, + "negative_test_case": false + }, + "remediation": { + "notes": "", + "transformation": null, + "modeling_rule": null + } } \ No newline at end of file diff --git a/PHP/38_invoke/1_instance_38_invoke/1_instance_38_invoke.php b/PHP/38_invoke/1_instance_38_invoke/1_instance_38_invoke.php old mode 100644 new mode 100755 index 3f709e1..ff9841d --- a/PHP/38_invoke/1_instance_38_invoke/1_instance_38_invoke.php +++ b/PHP/38_invoke/1_instance_38_invoke/1_instance_38_invoke.php @@ -1,13 +1,10 @@ - methods7.contains(x.code.toLowerCase)}.location.toJson); - println(x38) - delete; +@main def main(name : String): Unit = { + importCpg(name) + def methodClasses = cpg.method.name("__invoke").astParentFullName.l + val x38 = (name, "38_invoke_iall", cpg.call("NEW").argument.filter{x => methodClasses.contains(x.code.toLowerCase)}.location.toJson); + println(x38) + delete; } \ No newline at end of file diff --git a/PHP/38_invoke/38_invoke.json b/PHP/38_invoke/38_invoke.json old mode 100644 new mode 100755 index d3487dc..7d0d3d2 --- a/PHP/38_invoke/38_invoke.json +++ b/PHP/38_invoke/38_invoke.json @@ -1,14 +1,14 @@ -{ - "name": "Invoke", - "description": "", - "family": "code_pattern_php", - "tags": [ - "sast", - "php", - "php_v7.4.9" - ], - "instances": [ - "./1_instance_38_invoke/1_instance_38_invoke.json" - ], - "version": "v0.draft" +{ + "name": "Invoke", + "description": "./docs/description.md", + "family": "code_pattern_php", + "tags": [ + "sast", + "php", + "php_v7.4.9" + ], + "instances": [ + "./1_instance_38_invoke/1_instance_38_invoke.json" + ], + "version": "v1.0" } \ No newline at end of file diff --git a/PHP/38_invoke/Pattern Invoke.md b/PHP/38_invoke/Pattern Invoke.md deleted file mode 100644 index badfbf9..0000000 --- a/PHP/38_invoke/Pattern Invoke.md +++ /dev/null @@ -1,99 +0,0 @@ -# Pattern: Invoke - -## Category - -Objects - -## Definition - -## Instances - -### Instance 1 - -- CATEGORY: S0 -- FEATURE vs INTERNAL API: FEATURE -- INPUT SANITIZERS: NO -- SOURCES AND SINKS: NO -- NEGATIVE TEST CASES: NO -- CODE: - -```php - x.method.name.l.contains("__invoke")}.name.l -cpg.call("NEW").argument.filter{x => methods1.contains(x.code.toLowerCase)}.size; -``` - -- PRECONDITIONS: - 1. - -- TRANSFORMATION: - -Call the toString function manually. - -``` - -``` - diff --git a/PHP/38_invoke/README.md b/PHP/38_invoke/README.md new file mode 100755 index 0000000..2a8ccfa --- /dev/null +++ b/PHP/38_invoke/README.md @@ -0,0 +1,121 @@ +[//]: # (This file is automatically generated. If you wish to make any changes, please use the JSON files and regenerate this file using the tpframework.) + +# Invoke + +Tags: sast, php, php_v7.4.9 + +Version: v1.0 + +## Description + +`__invoke` is one of PHP [Magic Methods](https://www.php.net/manual/en/language.oop5.magic.php), that override PHP default actions. It is called when a script tries to call an object as a function. + +## Overview + +| Instances | has discovery rule | discovery method | rule successfull | +|---------------------------|----------------------|--------------------|--------------------| +| [1 Instance](#1-instance) | yes | joern | yes | + +## 1 Instance + +This instance shows, that when you have a callable object and you call the object, `__invoke` will be called. + +### Code + +```PHP + + +More + +
+ + +### Compile + + +```bash +$_main: + ; (lines=12, args=0, vars=3, tmps=8) + ; (before optimizer) + ; /.../PHP/38_invoke/1_instance_38_invoke/1_instance_38_invoke.php:1-10 + ; return [] RANGE[0..0] +0000 V3 = NEW 0 string("CallableClass") +0001 DO_FCALL +0002 ASSIGN CV0($obj) V3 +0003 T6 = FETCH_R (global) string("_GET") +0004 T7 = FETCH_DIM_R T6 string("p1") +0005 ASSIGN CV1($b) T7 +0006 INIT_DYNAMIC_CALL 1 CV0($obj) +0007 SEND_VAR_EX CV1($b) 1 +0008 V9 = DO_FCALL +0009 ASSIGN CV2($a) V9 +0010 ECHO CV2($a) +0011 RETURN int(1) +LIVE RANGES: + 3: 0001 - 0002 (new) + +CallableClass::__invoke: + ; (lines=3, args=1, vars=1, tmps=0) + ; (before optimizer) + ; /.../PHP/38_invoke/1_instance_38_invoke/1_instance_38_invoke.php:3-5 + ; return [] RANGE[0..0] +0000 CV0($x) = RECV 1 +0001 RETURN CV0($x) +0002 RETURN null +``` + +
+ +
+ + +### Discovery + + +The discovery rule first gets all class names, where the method `__invoke` is defined. +Afterwards it collects all `NEW` calles and filters them to see which of the instanciated objects has the method `__invoke` defined. +The rule would be perfect, if we could additionally check, if one of the created objects invokes the `__invoke` method within its lifetime. + +```scala +def methodClasses = cpg.method.name("__invoke").astParentFullName.l +val x38 = (name, "38_invoke_iall", cpg.call("NEW").argument.filter{x => methodClasses.contains(x.code.toLowerCase)}.location.toJson); +``` + +| discovery method | expected accuracy | +|--------------------|---------------------| +| joern | FP | + +
+ +
+ + +### Measurement + + +| Tool | Comm_1 | Comm_2 | phpSAFE | Progpilot | RIPS | WAP | Ground Truth | +|-------------|----------|----------|-----------|-------------|--------|-------|----------------| +| 08 Jun 2021 | yes | no | no | no | no | no | yes | +| 17 May 2023 | yes | | | | | | yes | + +
+ + diff --git a/PHP/38_invoke/docs/description.md b/PHP/38_invoke/docs/description.md new file mode 100755 index 0000000..139267f --- /dev/null +++ b/PHP/38_invoke/docs/description.md @@ -0,0 +1 @@ +`__invoke` is one of PHP [Magic Methods](https://www.php.net/manual/en/language.oop5.magic.php), that override PHP default actions. It is called when a script tries to call an object as a function. \ No newline at end of file diff --git a/PHP/39_serialize_unserialize/1_instance_39_serialize_unserialize/1_instance_39_serialize_unserialize.bash b/PHP/39_serialize_unserialize/1_instance_39_serialize_unserialize/1_instance_39_serialize_unserialize.bash old mode 100644 new mode 100755 index 1bfb547..e224872 --- a/PHP/39_serialize_unserialize/1_instance_39_serialize_unserialize/1_instance_39_serialize_unserialize.bash +++ b/PHP/39_serialize_unserialize/1_instance_39_serialize_unserialize/1_instance_39_serialize_unserialize.bash @@ -1,51 +1,47 @@ - -$_main: ; (lines=24, args=0, vars=4, tmps=11) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/38_serialize_unserialize/38_serialize_unserialize.php:1-19 -L0 (3): NOP -L1 (14): EXT_STMT -L2 (14): T4 = FETCH_R (global) string("_GET") -L3 (14): T5 = FETCH_DIM_R T4 string("p1") -L4 (14): ASSIGN CV0($b) T5 -L5 (15): EXT_STMT -L6 (15): V7 = NEW 1 string("A") -L7 (15): SEND_VAR_EX CV0($b) 1 -L8 (15): DO_FCALL -L9 (15): ASSIGN CV1($a) V7 -L10 (16): EXT_STMT -L11 (16): INIT_FCALL 1 96 string("serialize") -L12 (16): SEND_VAR CV1($a) 1 -L13 (16): V10 = DO_FCALL -L14 (16): ASSIGN CV2($s) V10 -L15 (17): EXT_STMT -L16 (17): INIT_FCALL 1 96 string("unserialize") -L17 (17): SEND_VAR CV2($s) 1 -L18 (17): V12 = DO_FCALL -L19 (17): ASSIGN CV3($h) V12 -L20 (19): EXT_STMT -L21 (19): INIT_METHOD_CALL 0 CV3($h) string("show_one") -L22 (19): DO_FCALL -L23 (19): RETURN int(1) -LIVE RANGES: - 7: L7 - L9 (new) - -A::__construct: ; (lines=7, args=1, vars=1, tmps=1) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/38_serialize_unserialize/38_serialize_unserialize.php:5-7 -L0 (5): EXT_NOP -L1 (5): CV0($b) = RECV 1 -L2 (6): EXT_STMT -L3 (6): ASSIGN_OBJ THIS string("one") -L4 (6): OP_DATA CV0($b) -L5 (7): EXT_STMT -L6 (7): RETURN null - -A::show_one: ; (lines=6, args=0, vars=0, tmps=1) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/38_serialize_unserialize/38_serialize_unserialize.php:9-11 -L0 (9): EXT_NOP -L1 (10): EXT_STMT -L2 (10): T0 = FETCH_OBJ_R THIS string("one") -L3 (10): ECHO T0 -L4 (11): EXT_STMT -L5 (11): RETURN null + +$_main: + ; (lines=20, args=0, vars=5, tmps=12) + ; (before optimizer) + ; /.../PHP/39_serialize_unserialize/1_instance_39_serialize_unserialize/1_instance_39_serialize_unserialize.php:1-20 + ; return [] RANGE[0..0] +0000 T5 = FETCH_R (global) string("_GET") +0001 T6 = FETCH_DIM_R T5 string("p1") +0002 ASSIGN CV0($b) T6 +0003 V8 = NEW 1 string("A") +0004 SEND_VAR_EX CV0($b) 1 +0005 DO_FCALL +0006 ASSIGN CV1($a) V8 +0007 INIT_FCALL 1 96 string("serialize") +0008 SEND_VAR CV1($a) 1 +0009 V11 = DO_ICALL +0010 ASSIGN CV2($s) V11 +0011 INIT_FCALL 1 96 string("unserialize") +0012 SEND_VAR CV2($s) 1 +0013 V13 = DO_ICALL +0014 ASSIGN CV3($h) V13 +0015 INIT_METHOD_CALL 0 CV3($h) string("show_one") +0016 V15 = DO_FCALL +0017 ASSIGN CV4($c) V15 +0018 ECHO CV4($c) +0019 RETURN int(1) +LIVE RANGES: + 8: 0004 - 0006 (new) + +A::__construct: + ; (lines=4, args=1, vars=1, tmps=1) + ; (before optimizer) + ; /.../PHP/39_serialize_unserialize/1_instance_39_serialize_unserialize/1_instance_39_serialize_unserialize.php:5-7 + ; return [] RANGE[0..0] +0000 CV0($b) = RECV 1 +0001 ASSIGN_OBJ THIS string("one") +0002 OP_DATA CV0($b) +0003 RETURN null + +A::show_one: + ; (lines=3, args=0, vars=0, tmps=1) + ; (before optimizer) + ; /.../PHP/39_serialize_unserialize/1_instance_39_serialize_unserialize/1_instance_39_serialize_unserialize.php:9-11 + ; return [] RANGE[0..0] +0000 T0 = FETCH_OBJ_R THIS string("one") +0001 RETURN T0 +0002 RETURN null diff --git a/PHP/39_serialize_unserialize/1_instance_39_serialize_unserialize/1_instance_39_serialize_unserialize.json b/PHP/39_serialize_unserialize/1_instance_39_serialize_unserialize/1_instance_39_serialize_unserialize.json old mode 100644 new mode 100755 index 382c7be..8fdec37 --- a/PHP/39_serialize_unserialize/1_instance_39_serialize_unserialize/1_instance_39_serialize_unserialize.json +++ b/PHP/39_serialize_unserialize/1_instance_39_serialize_unserialize/1_instance_39_serialize_unserialize.json @@ -1,37 +1,38 @@ -{ - "code": { - "path": "./1_instance_39_serialize_unserialize.php", - "injection_skeleton_broken": false - }, - "discovery": { - "rule": "./1_instance_39_serialize_unserialize.sc", - "method": "joern", - "rule_accuracy": null, - "notes": null - }, - "compile": { - "binary": "./1_instance_39_serialize_unserialize.bash", - "instruction": null, - "dependencies": null - }, - "expectation": { - "type": "xss", - "sink_file": "./1_instance_39_serialize_unserialize.php", - "sink_line": 10, - "source_file": "./1_instance_39_serialize_unserialize.php", - "source_line": 14, - "expectation": true - }, - "properties": { - "category": "S0", - "feature_vs_internal_api": "FEATURE", - "input_sanitizer": false, - "source_and_sink": false, - "negative_test_case": false - }, - "remediation": { - "notes": "", - "transformation": null, - "modeling_rule": null - } +{ + "description": "The instance shows `serialize` and `unserialize` called on an object as tarpits. After the object is unserialized, it has the same properties as before.", + "code": { + "path": "./1_instance_39_serialize_unserialize.php", + "injection_skeleton_broken": false + }, + "discovery": { + "rule": "./1_instance_39_serialize_unserialize.sc", + "method": "joern", + "rule_accuracy": "Perfect", + "notes": "The rule searches for a function call to serialize or unserialize in opcode." + }, + "compile": { + "binary": "./1_instance_39_serialize_unserialize.bash", + "instruction": null, + "dependencies": null + }, + "expectation": { + "type": "xss", + "sink_file": "./1_instance_39_serialize_unserialize.php", + "sink_line": 20, + "source_file": "./1_instance_39_serialize_unserialize.php", + "source_line": 14, + "expectation": true + }, + "properties": { + "category": "S0", + "feature_vs_internal_api": "FEATURE", + "input_sanitizer": false, + "source_and_sink": false, + "negative_test_case": false + }, + "remediation": { + "notes": "", + "transformation": null, + "modeling_rule": null + } } \ No newline at end of file diff --git a/PHP/39_serialize_unserialize/1_instance_39_serialize_unserialize/1_instance_39_serialize_unserialize.php b/PHP/39_serialize_unserialize/1_instance_39_serialize_unserialize/1_instance_39_serialize_unserialize.php old mode 100644 new mode 100755 index b120a99..e7c42d8 --- a/PHP/39_serialize_unserialize/1_instance_39_serialize_unserialize/1_instance_39_serialize_unserialize.php +++ b/PHP/39_serialize_unserialize/1_instance_39_serialize_unserialize/1_instance_39_serialize_unserialize.php @@ -1,19 +1,20 @@ -one = $b; - } - - public function show_one() { - echo $this->one; - } - } - -$b = $_GET["p1"]; -$a = new A($b); -$s = serialize($a); -$h = unserialize($s); -// will print $b, XSS vulnerability -$h->show_one(); \ No newline at end of file +one = $b; + } + + public function show_one() { + return $this->one; + } +} + +$b = $_GET["p1"]; // source +$a = new A($b); +$s = serialize($a); +$h = unserialize($s); +// will print $b, XSS vulnerability +$c = $h->show_one(); +echo $c; // sink \ No newline at end of file diff --git a/PHP/39_serialize_unserialize/1_instance_39_serialize_unserialize/1_instance_39_serialize_unserialize.sc b/PHP/39_serialize_unserialize/1_instance_39_serialize_unserialize/1_instance_39_serialize_unserialize.sc old mode 100644 new mode 100755 index 75ac84c..b8677d2 --- a/PHP/39_serialize_unserialize/1_instance_39_serialize_unserialize/1_instance_39_serialize_unserialize.sc +++ b/PHP/39_serialize_unserialize/1_instance_39_serialize_unserialize/1_instance_39_serialize_unserialize.sc @@ -1,6 +1,6 @@ -@main def main(name : String): Unit = { - importCpg(name) - val x39 = (name, "39_serialize_unserialize_iall", cpg.call("INIT_FCALL").argument.order(2).code("serialize|unserialize").astParent.location.toJson); - println(x39) - delete; +@main def main(name : String): Unit = { + importCpg(name) + val x39 = (name, "39_serialize_unserialize_iall", cpg.call("INIT_FCALL").argument.order(2).code("serialize|unserialize").astParent.location.toJson); + println(x39) + delete; } \ No newline at end of file diff --git a/PHP/39_serialize_unserialize/39_serialize_unserialize.json b/PHP/39_serialize_unserialize/39_serialize_unserialize.json old mode 100644 new mode 100755 index e3eaa33..a9121e4 --- a/PHP/39_serialize_unserialize/39_serialize_unserialize.json +++ b/PHP/39_serialize_unserialize/39_serialize_unserialize.json @@ -1,14 +1,14 @@ -{ - "name": "Serialize Unserialize", - "description": "", - "family": "code_pattern_php", - "tags": [ - "sast", - "php", - "php_v7.4.9" - ], - "instances": [ - "./1_instance_39_serialize_unserialize/1_instance_39_serialize_unserialize.json" - ], - "version": "v0.draft" +{ + "name": "Serialize Unserialize", + "description": "./docs/description.md", + "family": "code_pattern_php", + "tags": [ + "sast", + "php", + "php_v7.4.9" + ], + "instances": [ + "./1_instance_39_serialize_unserialize/1_instance_39_serialize_unserialize.json" + ], + "version": "v1.0" } \ No newline at end of file diff --git a/PHP/39_serialize_unserialize/Pattern Serialize and Unserialize.md b/PHP/39_serialize_unserialize/Pattern Serialize and Unserialize.md deleted file mode 100644 index e21e94e..0000000 --- a/PHP/39_serialize_unserialize/Pattern Serialize and Unserialize.md +++ /dev/null @@ -1,118 +0,0 @@ -# Pattern: Serialize and Unserialize - -## Category - -Objects - -## Definition - -## Instances - -### Instance 1 - -- CATEGORY: S0 -- FEATURE vs INTERNAL API: FEATURE -- INPUT SANITIZERS: NO -- SOURCES AND SINKS: NO -- NEGATIVE TEST CASES: NO -- CODE: - -```php -one = $b; - } - - public function show_one() { - echo $this->one; - } - } - -$b = $_GET["p1"]; -$a = new A($b); -$s = serialize($a); -$h = unserialize($s); -// will print $b, XSS vulnerability -$h->show_one(); -``` - -- MEASUREMENT: - -| Tool | RIPS | phpSAFE | WAP | Progpilot | Comm_1 | Comm_2 | Correct | -| ------------- | ---- | ------- | ---- | --------- | ------- | --------- | ------- | -| Vulnerability | NO | NO | NO | YES | NO | NO | YES | -Measurements Date: 8 June 2021 - -- OPCODE: - -```bash -$_main: ; (lines=24, args=0, vars=4, tmps=11) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/38_serialize_unserialize/38_serialize_unserialize.php:1-19 -L0 (3): NOP -L1 (14): EXT_STMT -L2 (14): T4 = FETCH_R (global) string("_GET") -L3 (14): T5 = FETCH_DIM_R T4 string("p1") -L4 (14): ASSIGN CV0($b) T5 -L5 (15): EXT_STMT -L6 (15): V7 = NEW 1 string("A") -L7 (15): SEND_VAR_EX CV0($b) 1 -L8 (15): DO_FCALL -L9 (15): ASSIGN CV1($a) V7 -L10 (16): EXT_STMT -L11 (16): INIT_FCALL 1 96 string("serialize") -L12 (16): SEND_VAR CV1($a) 1 -L13 (16): V10 = DO_FCALL -L14 (16): ASSIGN CV2($s) V10 -L15 (17): EXT_STMT -L16 (17): INIT_FCALL 1 96 string("unserialize") -L17 (17): SEND_VAR CV2($s) 1 -L18 (17): V12 = DO_FCALL -L19 (17): ASSIGN CV3($h) V12 -L20 (19): EXT_STMT -L21 (19): INIT_METHOD_CALL 0 CV3($h) string("show_one") -L22 (19): DO_FCALL -L23 (19): RETURN int(1) -LIVE RANGES: - 7: L7 - L9 (new) - -A::__construct: ; (lines=7, args=1, vars=1, tmps=1) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/38_serialize_unserialize/38_serialize_unserialize.php:5-7 -L0 (5): EXT_NOP -L1 (5): CV0($b) = RECV 1 -L2 (6): EXT_STMT -L3 (6): ASSIGN_OBJ THIS string("one") -L4 (6): OP_DATA CV0($b) -L5 (7): EXT_STMT -L6 (7): RETURN null - -A::show_one: ; (lines=6, args=0, vars=0, tmps=1) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/38_serialize_unserialize/38_serialize_unserialize.php:9-11 -L0 (9): EXT_NOP -L1 (10): EXT_STMT -L2 (10): T0 = FETCH_OBJ_R THIS string("one") -L3 (10): ECHO T0 -L4 (11): EXT_STMT -L5 (11): RETURN null -``` - -- DISCOVERY: - -```bash -cpg.call("INIT_FCALL").argument.order(2).code("serialize|unserialize").location.l -``` - -- PRECONDITIONS: - 1. - -- TRANSFORMATION: - -``` - -``` - diff --git a/PHP/39_serialize_unserialize/README.md b/PHP/39_serialize_unserialize/README.md new file mode 100755 index 0000000..8cef813 --- /dev/null +++ b/PHP/39_serialize_unserialize/README.md @@ -0,0 +1,146 @@ +[//]: # (This file is automatically generated. If you wish to make any changes, please use the JSON files and regenerate this file using the tpframework.) + +# Serialize Unserialize + +Tags: sast, php, php_v7.4.9 + +Version: v1.0 + +## Description + +In PHP `serialize()` can be used to bring an object in a storable format. `unserialize()` does the opposite and creates a PHP value from a stored representation. + +## Overview + +| Instances | has discovery rule | discovery method | rule successfull | +|---------------------------|----------------------|--------------------|--------------------| +| [1 Instance](#1-instance) | yes | joern | yes | + +## 1 Instance + +The instance shows `serialize` and `unserialize` called on an object as tarpits. After the object is unserialized, it has the same properties as before. + +### Code + +```PHP +one = $b; + } + + public function show_one() { + return $this->one; + } +} + +$b = $_GET["p1"]; // source +$a = new A($b); +$s = serialize($a); +$h = unserialize($s); +// will print $b, XSS vulnerability +$c = $h->show_one(); +echo $c; // sink +``` + +### Instance Properties + +| category | feature_vs_internal_api | input_sanitizer | negative_test_case | source_and_sink | +|------------|---------------------------|-------------------|----------------------|-------------------| +| S0 | FEATURE | no | no | no | + +
+ +More + +
+ + +### Compile + + +```bash +$_main: + ; (lines=20, args=0, vars=5, tmps=12) + ; (before optimizer) + ; /.../PHP/39_serialize_unserialize/1_instance_39_serialize_unserialize/1_instance_39_serialize_unserialize.php:1-20 + ; return [] RANGE[0..0] +0000 T5 = FETCH_R (global) string("_GET") +0001 T6 = FETCH_DIM_R T5 string("p1") +0002 ASSIGN CV0($b) T6 +0003 V8 = NEW 1 string("A") +0004 SEND_VAR_EX CV0($b) 1 +0005 DO_FCALL +0006 ASSIGN CV1($a) V8 +0007 INIT_FCALL 1 96 string("serialize") +0008 SEND_VAR CV1($a) 1 +0009 V11 = DO_ICALL +0010 ASSIGN CV2($s) V11 +0011 INIT_FCALL 1 96 string("unserialize") +0012 SEND_VAR CV2($s) 1 +0013 V13 = DO_ICALL +0014 ASSIGN CV3($h) V13 +0015 INIT_METHOD_CALL 0 CV3($h) string("show_one") +0016 V15 = DO_FCALL +0017 ASSIGN CV4($c) V15 +0018 ECHO CV4($c) +0019 RETURN int(1) +LIVE RANGES: + 8: 0004 - 0006 (new) + +A::__construct: + ; (lines=4, args=1, vars=1, tmps=1) + ; (before optimizer) + ; /.../PHP/39_serialize_unserialize/1_instance_39_serialize_unserialize/1_instance_39_serialize_unserialize.php:5-7 + ; return [] RANGE[0..0] +0000 CV0($b) = RECV 1 +0001 ASSIGN_OBJ THIS string("one") +0002 OP_DATA CV0($b) +0003 RETURN null + +A::show_one: + ; (lines=3, args=0, vars=0, tmps=1) + ; (before optimizer) + ; /.../PHP/39_serialize_unserialize/1_instance_39_serialize_unserialize/1_instance_39_serialize_unserialize.php:9-11 + ; return [] RANGE[0..0] +0000 T0 = FETCH_OBJ_R THIS string("one") +0001 RETURN T0 +0002 RETURN null +``` + +
+ +
+ + +### Discovery + + +The rule searches for a function call to serialize or unserialize in opcode. + +```scala +val x39 = (name, "39_serialize_unserialize_iall", cpg.call("INIT_FCALL").argument.order(2).code("serialize|unserialize").astParent.location.toJson); +``` + +| discovery method | expected accuracy | +|--------------------|---------------------| +| joern | Perfect | + +
+ +
+ + +### Measurement + + +| Tool | Comm_1 | Comm_2 | phpSAFE | Progpilot | RIPS | WAP | Ground Truth | +|-------------|----------|----------|-----------|-------------|--------|-------|----------------| +| 08 Jun 2021 | no | no | no | yes | no | no | yes | +| 17 May 2023 | no | yes | | | | | yes | + +
+ +
diff --git a/PHP/39_serialize_unserialize/docs/description.md b/PHP/39_serialize_unserialize/docs/description.md new file mode 100755 index 0000000..3f3df99 --- /dev/null +++ b/PHP/39_serialize_unserialize/docs/description.md @@ -0,0 +1 @@ +In PHP `serialize()` can be used to bring an object in a storable format. `unserialize()` does the opposite and creates a PHP value from a stored representation. \ No newline at end of file diff --git a/PHP/3_global_array/1_instance_3_global_array/1_instance_3_global_array.bash b/PHP/3_global_array/1_instance_3_global_array/1_instance_3_global_array.bash new file mode 100755 index 0000000..dd26d38 --- /dev/null +++ b/PHP/3_global_array/1_instance_3_global_array/1_instance_3_global_array.bash @@ -0,0 +1,23 @@ + +$_main: + ; (lines=8, args=0, vars=2, tmps=5) + ; (before optimizer) + ; /.../PHP/3_global_array/1_instance_3_global_array/1_instance_3_global_array.php:1-9 + ; return [] RANGE[0..0] +0000 T2 = FETCH_R (global) string("_GET") +0001 T3 = FETCH_DIM_R T2 string("p1") +0002 ASSIGN CV0($a) T3 +0003 INIT_FCALL 0 96 string("f") +0004 V5 = DO_UCALL +0005 ASSIGN CV1($b) V5 +0006 ECHO CV1($b) +0007 RETURN int(1) + +F: + ; (lines=3, args=0, vars=0, tmps=1) + ; (before optimizer) + ; /.../PHP/3_global_array/1_instance_3_global_array/1_instance_3_global_array.php:2-4 + ; return [] RANGE[0..0] +0000 T0 = FETCH_R (global) string("a") +0001 RETURN T0 +0002 RETURN null diff --git a/PHP/3_global_array/1_instance_3_global_array/1_instance_3_global_array.json b/PHP/3_global_array/1_instance_3_global_array/1_instance_3_global_array.json old mode 100644 new mode 100755 index 436f7af..9707fe3 --- a/PHP/3_global_array/1_instance_3_global_array/1_instance_3_global_array.json +++ b/PHP/3_global_array/1_instance_3_global_array/1_instance_3_global_array.json @@ -1,37 +1,38 @@ -{ - "code": { - "path": "./1_instance_3_global_array.php", - "injection_skeleton_broken": false - }, - "discovery": { - "rule": "./1_instance_3_global_array.sc", - "method": "joern", - "rule_accuracy": "FP", - "notes": "The discovery rule would be more accurate if we could check if the GLOBALS fetch is done outside the main block" - }, - "remediation": { - "notes": null, - "transformation": null, - "modeling_rule": null - }, - "compile": { - "binary": "", - "dependencies": null, - "instruction": null - }, - "expectation": { - "type": "xss", - "sink_file": "./1_instance_3_global_array.php", - "sink_line": 9, - "source_file": "./1_instance_3_global_array.php", - "source_line": 7, - "expectation": true - }, - "properties": { - "category": "S0", - "feature_vs_internal_api": "FEATURE", - "input_sanitizer": false, - "source_and_sink": false, - "negative_test_case": false - } +{ + "description": "This instance targets using the `GLOBALS` array within a function.", + "code": { + "path": "./1_instance_3_global_array.php", + "injection_skeleton_broken": false + }, + "discovery": { + "rule": "./1_instance_3_global_array.sc", + "method": "joern", + "rule_accuracy": "FP", + "notes": "The discovery rule would be more accurate if we could check if the GLOBALS fetch is done outside the main block" + }, + "remediation": { + "notes": "Most likely, this needs manual transformation, by passing the value as an argument to the function or using a class and an instance variable instead.", + "transformation": null, + "modeling_rule": null + }, + "compile": { + "binary": "./1_instance_3_global_array.bash", + "dependencies": null, + "instruction": null + }, + "expectation": { + "type": "xss", + "sink_file": "./1_instance_3_global_array.php", + "sink_line": 8, + "source_file": "./1_instance_3_global_array.php", + "source_line": 6, + "expectation": true + }, + "properties": { + "category": "S0", + "feature_vs_internal_api": "FEATURE", + "input_sanitizer": false, + "source_and_sink": false, + "negative_test_case": false + } } \ No newline at end of file diff --git a/PHP/3_global_array/1_instance_3_global_array/1_instance_3_global_array.php b/PHP/3_global_array/1_instance_3_global_array/1_instance_3_global_array.php old mode 100644 new mode 100755 index 0695f6f..4366c1f --- a/PHP/3_global_array/1_instance_3_global_array/1_instance_3_global_array.php +++ b/PHP/3_global_array/1_instance_3_global_array/1_instance_3_global_array.php @@ -1,9 +1,8 @@ - + + +## 1 Instance + + +This instance targets using the `GLOBALS` array within a function. + +### Code + +```PHP + + +More + +
+ + +### Compile + + +```bash +$_main: + ; (lines=8, args=0, vars=2, tmps=5) + ; (before optimizer) + ; /.../PHP/3_global_array/1_instance_3_global_array/1_instance_3_global_array.php:1-9 + ; return [] RANGE[0..0] +0000 T2 = FETCH_R (global) string("_GET") +0001 T3 = FETCH_DIM_R T2 string("p1") +0002 ASSIGN CV0($a) T3 +0003 INIT_FCALL 0 96 string("f") +0004 V5 = DO_UCALL +0005 ASSIGN CV1($b) V5 +0006 ECHO CV1($b) +0007 RETURN int(1) + +F: + ; (lines=3, args=0, vars=0, tmps=1) + ; (before optimizer) + ; /.../PHP/3_global_array/1_instance_3_global_array/1_instance_3_global_array.php:2-4 + ; return [] RANGE[0..0] +0000 T0 = FETCH_R (global) string("a") +0001 RETURN T0 +0002 RETURN null +``` + +
+ +
+ + +### Discovery + + +The discovery rule would be more accurate if we could check if the GLOBALS fetch is done outside the main block + +```scala +val x3 = (name, "3_global_array_i1", cpg.call(".*FETCH_W.*|.*FETCH_R.*|.*FETCH_RW.*").argument.code("GLOBALS").location.toJson); +``` + +| discovery method | expected accuracy | +|--------------------|---------------------| +| joern | FP | + +
+ +
+ + +### Measurement + + +| Tool | Comm_1 | Comm_2 | phpSAFE | Progpilot | RIPS | WAP | Ground Truth | +|-------------|----------|----------|-----------|-------------|--------|-------|----------------| +| 08 Jun 2021 | yes | yes | no | yes | no | yes | yes | +| 17 May 2023 | yes | no | | | | | yes | + +
+ +
+ + +### Remediation + + +Most likely, this needs manual transformation, by passing the value as an argument to the function or using a class and an instance variable instead. + +
+ + + + + +
+ + +## 2 Instance + + +This instance targets using the `GLOBALS` array outside of functions inside the main block. + +### Code + +```PHP + + +More + +
+ + +### Compile + + +```bash +$_main: + ; (lines=7, args=0, vars=2, tmps=5) + ; (before optimizer) + ; /.../PHP/3_global_array/2_instance_3_global_array/2_instance_3_global_array.php:1-4 + ; return [] RANGE[0..0] +0000 T2 = FETCH_R (global) string("_GET") +0001 T3 = FETCH_DIM_R T2 string("p1") +0002 ASSIGN CV0($a) T3 +0003 T5 = FETCH_R (global) string("a") +0004 ASSIGN CV1($b) T5 +0005 ECHO CV1($b) +0006 RETURN int(1) +``` + +
+ +
+ + +### Discovery + + +Using discovery rule of another instance. A specific discovery rule for this instance would be more accurate if we could check if the GLOBALS fetch is done INSIDE the main block + +```scala +val x3 = (name, "3_global_array_i2", cpg.call(".*FETCH_W.*|.*FETCH_R.*|.*FETCH_RW.*").argument.code("GLOBALS").location.toJson); +``` + +| discovery method | expected accuracy | +|--------------------|---------------------| +| joern | FP | + +
+ +
+ + +### Measurement + + +| Tool | Comm_1 | Comm_2 | Ground Truth | +|-------------|----------|----------|----------------| +| 17 May 2023 | yes | yes | yes | +| 26 May 2023 | yes | yes | yes | + +
+ +
+ + +### Remediation + + +Instead of using the `GLOBALS` array, one could transform the code to use the 'normal' name of the variable instead + +
+ +
+ + diff --git a/PHP/3_global_array/docs/description.md b/PHP/3_global_array/docs/description.md new file mode 100755 index 0000000..e798afe --- /dev/null +++ b/PHP/3_global_array/docs/description.md @@ -0,0 +1 @@ +This pattern targets [`GLOBALS`](https://www.php.net/manual/en/reserved.variables.globals.php) array. This array contains references to all variables which are defined in the global scope. \ No newline at end of file diff --git a/PHP/40_trait/1_instance_40_trait/1_instance_40_trait.bash b/PHP/40_trait/1_instance_40_trait/1_instance_40_trait.bash old mode 100644 new mode 100755 index 1fdd146..62dbdb3 --- a/PHP/40_trait/1_instance_40_trait/1_instance_40_trait.bash +++ b/PHP/40_trait/1_instance_40_trait/1_instance_40_trait.bash @@ -1,30 +1,30 @@ - -$_main: ; (lines=14, args=0, vars=2, tmps=7) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/67_trait/67_trait.php:1-16 -L0 (2): EXT_STMT -L1 (2): T2 = FETCH_R (global) string("_GET") -L2 (2): T3 = FETCH_DIM_R T2 string("p1") -L3 (2): ASSIGN CV0($b) T3 -L4 (10): DECLARE_CLASS string("myhelloworld") -L5 (14): EXT_STMT -L6 (14): V5 = NEW 0 string("MyHelloWorld") -L7 (14): DO_FCALL -L8 (14): ASSIGN CV1($o) V5 -L9 (16): EXT_STMT -L10 (16): INIT_METHOD_CALL 1 CV1($o) string("sayHello") -L11 (16): SEND_VAR_EX CV0($b) 1 -L12 (16): DO_FCALL -L13 (16): RETURN int(1) -LIVE RANGES: - 5: L7 - L8 (new) - -SayWorld::sayHello: ; (lines=6, args=1, vars=1, tmps=0) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/67_trait/67_trait.php:5-7 -L0 (5): EXT_NOP -L1 (5): CV0($b) = RECV 1 -L2 (6): EXT_STMT -L3 (6): ECHO CV0($b) -L4 (7): EXT_STMT -L5 (7): RETURN null + +$_main: + ; (lines=13, args=0, vars=3, tmps=8) + ; (before optimizer) + ; /.../PHP/40_trait/1_instance_40_trait/1_instance_40_trait.php:1-16 + ; return [] RANGE[0..0] +0000 DECLARE_CLASS string("myhelloworld") +0001 T3 = FETCH_R (global) string("_GET") +0002 T4 = FETCH_DIM_R T3 string("p1") +0003 ASSIGN CV0($b) T4 +0004 V6 = NEW 0 string("MyHelloWorld") +0005 DO_FCALL +0006 ASSIGN CV1($o) V6 +0007 INIT_METHOD_CALL 1 CV1($o) string("sayHello") +0008 SEND_VAR_EX CV0($b) 1 +0009 V9 = DO_FCALL +0010 ASSIGN CV2($a) V9 +0011 ECHO CV2($a) +0012 RETURN int(1) +LIVE RANGES: + 6: 0005 - 0006 (new) + +SayWorld::sayHello: + ; (lines=3, args=1, vars=1, tmps=0) + ; (before optimizer) + ; /.../PHP/40_trait/1_instance_40_trait/1_instance_40_trait.php:3-5 + ; return [] RANGE[0..0] +0000 CV0($b) = RECV 1 +0001 RETURN CV0($b) +0002 RETURN null diff --git a/PHP/40_trait/1_instance_40_trait/1_instance_40_trait.json b/PHP/40_trait/1_instance_40_trait/1_instance_40_trait.json old mode 100644 new mode 100755 index dc3a75e..f9876d9 --- a/PHP/40_trait/1_instance_40_trait/1_instance_40_trait.json +++ b/PHP/40_trait/1_instance_40_trait/1_instance_40_trait.json @@ -1,37 +1,38 @@ -{ - "code": { - "path": "./1_instance_40_trait.php", - "injection_skeleton_broken": false - }, - "discovery": { - "rule": "./1_instance_40_trait.sc", - "method": "joern", - "rule_accuracy": null, - "notes": null - }, - "compile": { - "binary": "./1_instance_40_trait.bash", - "instruction": null, - "dependencies": null - }, - "expectation": { - "type": "xss", - "sink_file": "./1_instance_40_trait.php", - "sink_line": 6, - "source_file": "./1_instance_40_trait.php", - "source_line": 2, - "expectation": true - }, - "properties": { - "category": "S0", - "feature_vs_internal_api": "FEATURE", - "input_sanitizer": false, - "source_and_sink": false, - "negative_test_case": false - }, - "remediation": { - "notes": "", - "transformation": null, - "modeling_rule": null - } +{ + "description": "The instance shows, that the trait `SayWorld` is used by `MyHelloWorld`.", + "code": { + "path": "./1_instance_40_trait.php", + "injection_skeleton_broken": false + }, + "discovery": { + "rule": "./1_instance_40_trait.sc", + "method": "joern", + "rule_accuracy": "FP", + "notes": "The rule searches for class declarations (`DECLARE_CLASS`) in opcode. It would be more accurate, if a trait was involved. This pattern might profit from source code detection as well." + }, + "compile": { + "binary": "./1_instance_40_trait.bash", + "instruction": null, + "dependencies": null + }, + "expectation": { + "type": "xss", + "sink_file": "./1_instance_40_trait.php", + "sink_line": 16, + "source_file": "./1_instance_40_trait.php", + "source_line": 12, + "expectation": true + }, + "properties": { + "category": "S0", + "feature_vs_internal_api": "FEATURE", + "input_sanitizer": false, + "source_and_sink": false, + "negative_test_case": false + }, + "remediation": { + "notes": "", + "transformation": null, + "modeling_rule": null + } } \ No newline at end of file diff --git a/PHP/40_trait/1_instance_40_trait/1_instance_40_trait.php b/PHP/40_trait/1_instance_40_trait/1_instance_40_trait.php old mode 100644 new mode 100755 index e8cd522..585e82d --- a/PHP/40_trait/1_instance_40_trait/1_instance_40_trait.php +++ b/PHP/40_trait/1_instance_40_trait/1_instance_40_trait.php @@ -1,16 +1,16 @@ -sayHello($b); \ No newline at end of file +sayHello($b); +echo $a; // sink \ No newline at end of file diff --git a/PHP/40_trait/1_instance_40_trait/1_instance_40_trait.sc b/PHP/40_trait/1_instance_40_trait/1_instance_40_trait.sc old mode 100644 new mode 100755 index 80f1e2f..0918768 --- a/PHP/40_trait/1_instance_40_trait/1_instance_40_trait.sc +++ b/PHP/40_trait/1_instance_40_trait/1_instance_40_trait.sc @@ -1,6 +1,6 @@ -@main def main(name : String): Unit = { - importCpg(name) - val x40 = (name, "40_trait_iall", cpg.call(".*DECLARE_CLASS.*").location.toJson); - println(x40) - delete; +@main def main(name : String): Unit = { + importCpg(name) + val x40 = (name, "40_trait_iall", cpg.call(".*DECLARE_CLASS.*").location.toJson); + println(x40) + delete; } \ No newline at end of file diff --git a/PHP/40_trait/40_trait.json b/PHP/40_trait/40_trait.json old mode 100644 new mode 100755 index a5725fb..a95096d --- a/PHP/40_trait/40_trait.json +++ b/PHP/40_trait/40_trait.json @@ -1,14 +1,14 @@ -{ - "name": "Trait", - "description": "", - "family": "code_pattern_php", - "tags": [ - "sast", - "php", - "php_v7.4.9" - ], - "instances": [ - "./1_instance_40_trait/1_instance_40_trait.json" - ], - "version": "v0.draft" +{ + "name": "Trait", + "description": "./docs/description.md", + "family": "code_pattern_php", + "tags": [ + "sast", + "php", + "php_v7.4.9" + ], + "instances": [ + "./1_instance_40_trait/1_instance_40_trait.json" + ], + "version": "v1.0" } \ No newline at end of file diff --git a/PHP/40_trait/Pattern Trait.md b/PHP/40_trait/Pattern Trait.md deleted file mode 100644 index 10bb7c0..0000000 --- a/PHP/40_trait/Pattern Trait.md +++ /dev/null @@ -1,108 +0,0 @@ -# Pattern: Trait - -## Category - -Objects - -## Definition - -## Instances - -### Instance 1 - -- CATEGORY: S0 -- FEATURE vs INTERNAL API: FEATURE -- INPUT SANITIZERS: NO -- SOURCES AND SINKS: NO -- NEGATIVE TEST CASES: NO -- CODE: - -```php -sayHello($b); -``` - -- MEASUREMENT: - -| Tool | RIPS | phpSAFE | WAP | Progpilot | Comm_1 | Comm_2 | Correct | -| ------------- | ---- | ------- | ---- | --------- | ------- | --------- | ------- | -| Vulnerability | NO | NO | NO | NO | YES | YES | YES | -Measurements Date: 8 June 2021 - -- OPCODE: - -```bash -$_main: ; (lines=14, args=0, vars=2, tmps=7) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/67_trait/67_trait.php:1-16 -L0 (2): EXT_STMT -L1 (2): T2 = FETCH_R (global) string("_GET") -L2 (2): T3 = FETCH_DIM_R T2 string("p1") -L3 (2): ASSIGN CV0($b) T3 -L4 (10): DECLARE_CLASS string("myhelloworld") -L5 (14): EXT_STMT -L6 (14): V5 = NEW 0 string("MyHelloWorld") -L7 (14): DO_FCALL -L8 (14): ASSIGN CV1($o) V5 -L9 (16): EXT_STMT -L10 (16): INIT_METHOD_CALL 1 CV1($o) string("sayHello") -L11 (16): SEND_VAR_EX CV0($b) 1 -L12 (16): DO_FCALL -L13 (16): RETURN int(1) -LIVE RANGES: - 5: L7 - L8 (new) - -SayWorld::sayHello: ; (lines=6, args=1, vars=1, tmps=0) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/67_trait/67_trait.php:5-7 -L0 (5): EXT_NOP -L1 (5): CV0($b) = RECV 1 -L2 (6): EXT_STMT -L3 (6): ECHO CV0($b) -L4 (7): EXT_STMT -L5 (7): RETURN null -``` - -- DISCOVERY: - -```bash -cpg.call(".*DECLARE_CLASS.*").location.l -``` - -- PRECONDITIONS: - 1. -- TRANSFORMATION: - -Redefine the functions inside the classes. - -```php -sayHello($b); -``` - - - diff --git a/PHP/40_trait/README.md b/PHP/40_trait/README.md new file mode 100755 index 0000000..3d19504 --- /dev/null +++ b/PHP/40_trait/README.md @@ -0,0 +1,125 @@ +[//]: # (This file is automatically generated. If you wish to make any changes, please use the JSON files and regenerate this file using the tpframework.) + +# Trait + +Tags: sast, php, php_v7.4.9 + +Version: v1.0 + +## Description + +PHP only supports single inheritance. A trait can define methods to be used in multiple classes. This makes it possible for a class in PHP to inherit multiple behaviours. + +## Overview + +| Instances | has discovery rule | discovery method | rule successfull | +|---------------------------|----------------------|--------------------|--------------------| +| [1 Instance](#1-instance) | yes | joern | yes | + +## 1 Instance + +The instance shows, that the trait `SayWorld` is used by `MyHelloWorld`. + +### Code + +```PHP +sayHello($b); +echo $a; // sink +``` + +### Instance Properties + +| category | feature_vs_internal_api | input_sanitizer | negative_test_case | source_and_sink | +|------------|---------------------------|-------------------|----------------------|-------------------| +| S0 | FEATURE | no | no | no | + +
+ +More + +
+ + +### Compile + + +```bash +$_main: + ; (lines=13, args=0, vars=3, tmps=8) + ; (before optimizer) + ; /.../PHP/40_trait/1_instance_40_trait/1_instance_40_trait.php:1-16 + ; return [] RANGE[0..0] +0000 DECLARE_CLASS string("myhelloworld") +0001 T3 = FETCH_R (global) string("_GET") +0002 T4 = FETCH_DIM_R T3 string("p1") +0003 ASSIGN CV0($b) T4 +0004 V6 = NEW 0 string("MyHelloWorld") +0005 DO_FCALL +0006 ASSIGN CV1($o) V6 +0007 INIT_METHOD_CALL 1 CV1($o) string("sayHello") +0008 SEND_VAR_EX CV0($b) 1 +0009 V9 = DO_FCALL +0010 ASSIGN CV2($a) V9 +0011 ECHO CV2($a) +0012 RETURN int(1) +LIVE RANGES: + 6: 0005 - 0006 (new) + +SayWorld::sayHello: + ; (lines=3, args=1, vars=1, tmps=0) + ; (before optimizer) + ; /.../PHP/40_trait/1_instance_40_trait/1_instance_40_trait.php:3-5 + ; return [] RANGE[0..0] +0000 CV0($b) = RECV 1 +0001 RETURN CV0($b) +0002 RETURN null +``` + +
+ +
+ + +### Discovery + + +The rule searches for class declarations (`DECLARE_CLASS`) in opcode. It would be more accurate, if a trait was involved. This pattern might profit from source code detection as well. + +```scala +val x40 = (name, "40_trait_iall", cpg.call(".*DECLARE_CLASS.*").location.toJson); +``` + +| discovery method | expected accuracy | +|--------------------|---------------------| +| joern | FP | + +
+ +
+ + +### Measurement + + +| Tool | Comm_1 | Comm_2 | phpSAFE | Progpilot | RIPS | WAP | Ground Truth | +|-------------|----------|----------|-----------|-------------|--------|-------|----------------| +| 08 Jun 2021 | yes | yes | no | no | no | no | yes | +| 17 May 2023 | yes | yes | | | | | yes | + +
+ +
diff --git a/PHP/40_trait/docs/description.md b/PHP/40_trait/docs/description.md new file mode 100755 index 0000000..4b7723f --- /dev/null +++ b/PHP/40_trait/docs/description.md @@ -0,0 +1 @@ +PHP only supports single inheritance. A trait can define methods to be used in multiple classes. This makes it possible for a class in PHP to inherit multiple behaviours. \ No newline at end of file diff --git a/PHP/41_self_methods/1_instance_41_self_methods/1_instance_41_self_methods.bash b/PHP/41_self_methods/1_instance_41_self_methods/1_instance_41_self_methods.bash old mode 100644 new mode 100755 index a7051c4..cb6c5fb --- a/PHP/41_self_methods/1_instance_41_self_methods/1_instance_41_self_methods.bash +++ b/PHP/41_self_methods/1_instance_41_self_methods/1_instance_41_self_methods.bash @@ -1,40 +1,41 @@ - -$_main: ; (lines=12, args=0, vars=1, tmps=6) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/90_self_methods/90_self_methods.php:1-17 -L0 (15): EXT_STMT -L1 (15): V1 = NEW 0 string("myclass") -L2 (15): DO_FCALL -L3 (15): ASSIGN CV0($obj) V1 -L4 (17): EXT_STMT -L5 (17): INIT_METHOD_CALL 1 CV0($obj) string("F") -L6 (17): CHECK_FUNC_ARG 1 -L7 (17): V4 = FETCH_FUNC_ARG (global) string("_GET") -L8 (17): V5 = FETCH_DIM_FUNC_ARG V4 string("p1") -L9 (17): SEND_FUNC_ARG V5 1 -L10 (17): DO_FCALL -L11 (17): RETURN int(1) -LIVE RANGES: - 1: L2 - L3 (new) - -myclass::F: ; (lines=8, args=1, vars=1, tmps=1) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/90_self_methods/90_self_methods.php:5-7 -L0 (5): EXT_NOP -L1 (5): CV0($b) = RECV 1 -L2 (6): EXT_STMT -L3 (6): INIT_STATIC_METHOD_CALL 1 (self) (exception) string("T") -L4 (6): SEND_VAR_EX CV0($b) 1 -L5 (6): DO_FCALL -L6 (7): EXT_STMT -L7 (7): RETURN null - -myclass::T: ; (lines=6, args=1, vars=1, tmps=0) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/90_self_methods/90_self_methods.php:9-11 -L0 (9): EXT_NOP -L1 (9): CV0($b) = RECV 1 -L2 (10): EXT_STMT -L3 (10): ECHO CV0($b) -L4 (11): EXT_STMT -L5 (11): RETURN null + +$_main: + ; (lines=12, args=0, vars=3, tmps=8) + ; (before optimizer) + ; /.../PHP/41_self_methods/1_instance_41_self_methods/1_instance_41_self_methods.php:1-16 + ; return [] RANGE[0..0] +0000 T3 = FETCH_R (global) string("_GET") +0001 T4 = FETCH_DIM_R T3 string("p1") +0002 ASSIGN CV0($b) T4 +0003 V6 = NEW 0 string("myclass") +0004 DO_FCALL +0005 ASSIGN CV1($obj) V6 +0006 INIT_METHOD_CALL 1 CV1($obj) string("F") +0007 SEND_VAR_EX CV0($b) 1 +0008 V9 = DO_FCALL +0009 ASSIGN CV2($a) V9 +0010 ECHO CV2($a) +0011 RETURN int(1) +LIVE RANGES: + 6: 0004 - 0005 (new) + +myclass::F: + ; (lines=6, args=1, vars=1, tmps=1) + ; (before optimizer) + ; /.../PHP/41_self_methods/1_instance_41_self_methods/1_instance_41_self_methods.php:4-6 + ; return [] RANGE[0..0] +0000 CV0($b) = RECV 1 +0001 INIT_STATIC_METHOD_CALL 1 (self) (exception) string("T") +0002 SEND_VAR_EX CV0($b) 1 +0003 V1 = DO_FCALL +0004 RETURN V1 +0005 RETURN null + +myclass::T: + ; (lines=3, args=1, vars=1, tmps=0) + ; (before optimizer) + ; /.../PHP/41_self_methods/1_instance_41_self_methods/1_instance_41_self_methods.php:8-10 + ; return [] RANGE[0..0] +0000 CV0($b) = RECV 1 +0001 RETURN CV0($b) +0002 RETURN null diff --git a/PHP/41_self_methods/1_instance_41_self_methods/1_instance_41_self_methods.json b/PHP/41_self_methods/1_instance_41_self_methods/1_instance_41_self_methods.json old mode 100644 new mode 100755 index bc52a8e..f1b3229 --- a/PHP/41_self_methods/1_instance_41_self_methods/1_instance_41_self_methods.json +++ b/PHP/41_self_methods/1_instance_41_self_methods/1_instance_41_self_methods.json @@ -1,37 +1,38 @@ -{ - "code": { - "path": "./1_instance_41_self_methods.php", - "injection_skeleton_broken": false - }, - "discovery": { - "rule": "./1_instance_41_self_methods.sc", - "method": "joern", - "rule_accuracy": null, - "notes": null - }, - "compile": { - "binary": "./1_instance_41_self_methods.bash", - "instruction": null, - "dependencies": null - }, - "expectation": { - "type": "xss", - "sink_file": "./1_instance_41_self_methods.php", - "sink_line": 10, - "source_file": "./1_instance_41_self_methods.php", - "source_line": 17, - "expectation": true - }, - "properties": { - "category": "S0", - "feature_vs_internal_api": "FEATURE", - "input_sanitizer": false, - "source_and_sink": false, - "negative_test_case": false - }, - "remediation": { - "notes": "", - "transformation": null, - "modeling_rule": null - } +{ + "description": "The instance shows, how to call a static function of a class from a non static function of that class.", + "code": { + "path": "./1_instance_41_self_methods.php", + "injection_skeleton_broken": false + }, + "discovery": { + "rule": "./1_instance_41_self_methods.sc", + "method": "joern", + "rule_accuracy": "FP", + "notes": "The rule looks for static method calls (`INIT_STATIC_METHOD_CALL`) in the opcode and checks if the argument of that function is `self`." + }, + "compile": { + "binary": "./1_instance_41_self_methods.bash", + "instruction": null, + "dependencies": null + }, + "expectation": { + "type": "xss", + "sink_file": "./1_instance_41_self_methods.php", + "sink_line": 16, + "source_file": "./1_instance_41_self_methods.php", + "source_line": 13, + "expectation": true + }, + "properties": { + "category": "S0", + "feature_vs_internal_api": "FEATURE", + "input_sanitizer": false, + "source_and_sink": false, + "negative_test_case": false + }, + "remediation": { + "notes": "", + "transformation": null, + "modeling_rule": null + } } \ No newline at end of file diff --git a/PHP/41_self_methods/1_instance_41_self_methods/1_instance_41_self_methods.php b/PHP/41_self_methods/1_instance_41_self_methods/1_instance_41_self_methods.php old mode 100644 new mode 100755 index a7efa35..ab353d7 --- a/PHP/41_self_methods/1_instance_41_self_methods/1_instance_41_self_methods.php +++ b/PHP/41_self_methods/1_instance_41_self_methods/1_instance_41_self_methods.php @@ -1,17 +1,16 @@ -F($_GET['p1']); \ No newline at end of file +F($b); +echo $a; // sink \ No newline at end of file diff --git a/PHP/41_self_methods/1_instance_41_self_methods/1_instance_41_self_methods.sc b/PHP/41_self_methods/1_instance_41_self_methods/1_instance_41_self_methods.sc old mode 100644 new mode 100755 index 8e13a26..795c51c --- a/PHP/41_self_methods/1_instance_41_self_methods/1_instance_41_self_methods.sc +++ b/PHP/41_self_methods/1_instance_41_self_methods/1_instance_41_self_methods.sc @@ -1,6 +1,6 @@ -@main def main(name : String): Unit = { - importCpg(name) - val x41 = (name, "41_self_methods_iall", cpg.call(".*INIT_STATIC_METHOD_CALL.*").argument.order(1).code("self").astParent.location.toJson); - println(x41) - delete; +@main def main(name : String): Unit = { + importCpg(name) + val x41 = (name, "41_self_methods_iall", cpg.call(".*INIT_STATIC_METHOD_CALL.*").argument.order(1).code("self").astParent.location.toJson); + println(x41) + delete; } \ No newline at end of file diff --git a/PHP/41_self_methods/41_self_methods.json b/PHP/41_self_methods/41_self_methods.json old mode 100644 new mode 100755 index dd3598b..1d9c227 --- a/PHP/41_self_methods/41_self_methods.json +++ b/PHP/41_self_methods/41_self_methods.json @@ -1,14 +1,14 @@ -{ - "name": "Self Methods", - "description": "", - "family": "code_pattern_php", - "tags": [ - "sast", - "php", - "php_v7.4.9" - ], - "instances": [ - "./1_instance_41_self_methods/1_instance_41_self_methods.json" - ], - "version": "v0.draft" +{ + "name": "Self Methods", + "description": "./docs/description.md", + "family": "code_pattern_php", + "tags": [ + "sast", + "php", + "php_v7.4.9" + ], + "instances": [ + "./1_instance_41_self_methods/1_instance_41_self_methods.json" + ], + "version": "v1.0" } \ No newline at end of file diff --git a/PHP/41_self_methods/Pattern Self Methods.md b/PHP/41_self_methods/Pattern Self Methods.md deleted file mode 100644 index 7b955d8..0000000 --- a/PHP/41_self_methods/Pattern Self Methods.md +++ /dev/null @@ -1,121 +0,0 @@ -# Pattern: Self Methods - -## Category - -Objects - -## Definition - -## Instances - -### Instance 1 - -- CATEGORY: S0 -- FEATURE vs INTERNAL API: FEATURE -- INPUT SANITIZERS: NO -- SOURCES AND SINKS: NO -- NEGATIVE TEST CASES: NO -- CODE: - -```php -F($_GET['p1']); -``` - -- MEASUREMENT: - -| Tool | RIPS | phpSAFE | WAP | Progpilot | Comm_1 | Comm_2 | Correct | -| ------------- | ---- | ------- | ---- | --------- | ------- | --------- | ------- | -| Vulnerability | NO |NO | NO | YES | YES | NO | YES | -Measurements Date: 8 June 2021 - -- OPCODE: - -```bash -$_main: ; (lines=12, args=0, vars=1, tmps=6) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/90_self_methods/90_self_methods.php:1-17 -L0 (15): EXT_STMT -L1 (15): V1 = NEW 0 string("myclass") -L2 (15): DO_FCALL -L3 (15): ASSIGN CV0($obj) V1 -L4 (17): EXT_STMT -L5 (17): INIT_METHOD_CALL 1 CV0($obj) string("F") -L6 (17): CHECK_FUNC_ARG 1 -L7 (17): V4 = FETCH_FUNC_ARG (global) string("_GET") -L8 (17): V5 = FETCH_DIM_FUNC_ARG V4 string("p1") -L9 (17): SEND_FUNC_ARG V5 1 -L10 (17): DO_FCALL -L11 (17): RETURN int(1) -LIVE RANGES: - 1: L2 - L3 (new) - -myclass::F: ; (lines=8, args=1, vars=1, tmps=1) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/90_self_methods/90_self_methods.php:5-7 -L0 (5): EXT_NOP -L1 (5): CV0($b) = RECV 1 -L2 (6): EXT_STMT -L3 (6): INIT_STATIC_METHOD_CALL 1 (self) (exception) string("T") -L4 (6): SEND_VAR_EX CV0($b) 1 -L5 (6): DO_FCALL -L6 (7): EXT_STMT -L7 (7): RETURN null - -myclass::T: ; (lines=6, args=1, vars=1, tmps=0) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/90_self_methods/90_self_methods.php:9-11 -L0 (9): EXT_NOP -L1 (9): CV0($b) = RECV 1 -L2 (10): EXT_STMT -L3 (10): ECHO CV0($b) -L4 (11): EXT_STMT -L5 (11): RETURN null -``` - -- DISCOVERY: - -```bash -cpg.call(".*INIT_STATIC_METHOD_CALL.*").argument.order(1).code("self").location.l -``` - -- PRECONDITIONS: - 1. - -- TRANSFORMATION: - -```php -T($b); - } - - function T($b){ - echo $b; - } -} - - -$obj = new myclass(); - -$obj->F($_GET['p1']); -``` - diff --git a/PHP/41_self_methods/README.md b/PHP/41_self_methods/README.md new file mode 100755 index 0000000..f1f19d8 --- /dev/null +++ b/PHP/41_self_methods/README.md @@ -0,0 +1,136 @@ +[//]: # (This file is automatically generated. If you wish to make any changes, please use the JSON files and regenerate this file using the tpframework.) + +# Self Methods + +Tags: sast, php, php_v7.4.9 + +Version: v1.0 + +## Description + +In PHP `self` is used to refer to the current class. So `self::method` refers to a static method defined in the current class. + +## Overview + +| Instances | has discovery rule | discovery method | rule successfull | +|---------------------------|----------------------|--------------------|--------------------| +| [1 Instance](#1-instance) | yes | joern | yes | + +## 1 Instance + +The instance shows, how to call a static function of a class from a non static function of that class. + +### Code + +```PHP +F($b); +echo $a; // sink +``` + +### Instance Properties + +| category | feature_vs_internal_api | input_sanitizer | negative_test_case | source_and_sink | +|------------|---------------------------|-------------------|----------------------|-------------------| +| S0 | FEATURE | no | no | no | + +
+ +More + +
+ + +### Compile + + +```bash +$_main: + ; (lines=12, args=0, vars=3, tmps=8) + ; (before optimizer) + ; /.../PHP/41_self_methods/1_instance_41_self_methods/1_instance_41_self_methods.php:1-16 + ; return [] RANGE[0..0] +0000 T3 = FETCH_R (global) string("_GET") +0001 T4 = FETCH_DIM_R T3 string("p1") +0002 ASSIGN CV0($b) T4 +0003 V6 = NEW 0 string("myclass") +0004 DO_FCALL +0005 ASSIGN CV1($obj) V6 +0006 INIT_METHOD_CALL 1 CV1($obj) string("F") +0007 SEND_VAR_EX CV0($b) 1 +0008 V9 = DO_FCALL +0009 ASSIGN CV2($a) V9 +0010 ECHO CV2($a) +0011 RETURN int(1) +LIVE RANGES: + 6: 0004 - 0005 (new) + +myclass::F: + ; (lines=6, args=1, vars=1, tmps=1) + ; (before optimizer) + ; /.../PHP/41_self_methods/1_instance_41_self_methods/1_instance_41_self_methods.php:4-6 + ; return [] RANGE[0..0] +0000 CV0($b) = RECV 1 +0001 INIT_STATIC_METHOD_CALL 1 (self) (exception) string("T") +0002 SEND_VAR_EX CV0($b) 1 +0003 V1 = DO_FCALL +0004 RETURN V1 +0005 RETURN null + +myclass::T: + ; (lines=3, args=1, vars=1, tmps=0) + ; (before optimizer) + ; /.../PHP/41_self_methods/1_instance_41_self_methods/1_instance_41_self_methods.php:8-10 + ; return [] RANGE[0..0] +0000 CV0($b) = RECV 1 +0001 RETURN CV0($b) +0002 RETURN null +``` + +
+ +
+ + +### Discovery + + +The rule looks for static method calls (`INIT_STATIC_METHOD_CALL`) in the opcode and checks if the argument of that function is `self`. + +```scala +val x41 = (name, "41_self_methods_iall", cpg.call(".*INIT_STATIC_METHOD_CALL.*").argument.order(1).code("self").astParent.location.toJson); +``` + +| discovery method | expected accuracy | +|--------------------|---------------------| +| joern | FP | + +
+ +
+ + +### Measurement + + +| Tool | Comm_1 | Comm_2 | phpSAFE | Progpilot | RIPS | WAP | Ground Truth | +|-------------|----------|----------|-----------|-------------|--------|-------|----------------| +| 08 Jun 2021 | yes | no | no | yes | no | no | yes | +| 22 May 2023 | yes | yes | | | | | yes | + +
+ +
diff --git a/PHP/41_self_methods/docs/description.md b/PHP/41_self_methods/docs/description.md new file mode 100755 index 0000000..ad13542 --- /dev/null +++ b/PHP/41_self_methods/docs/description.md @@ -0,0 +1 @@ +In PHP `self` is used to refer to the current class. So `self::method` refers to a static method defined in the current class. \ No newline at end of file diff --git a/PHP/42_destructor/1_instance_42_destructor/1_instance_42_destructor.bash b/PHP/42_destructor/1_instance_42_destructor/1_instance_42_destructor.bash old mode 100644 new mode 100755 index 03dc587..be0a2a5 --- a/PHP/42_destructor/1_instance_42_destructor/1_instance_42_destructor.bash +++ b/PHP/42_destructor/1_instance_42_destructor/1_instance_42_destructor.bash @@ -1,45 +1,35 @@ - -$_main: ; (lines=11, args=0, vars=2, tmps=6) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/28_destructor/28_destructor.php:1-19 -L0 (4): NOP -L1 (16): EXT_STMT -L2 (16): T2 = FETCH_R (global) string("_GET") -L3 (16): T3 = FETCH_DIM_R T2 string("p1") -L4 (16): ASSIGN CV0($b) T3 -L5 (19): EXT_STMT -L6 (19): V5 = NEW 1 string("BaseClass") -L7 (19): SEND_VAR_EX CV0($b) 1 -L8 (19): DO_FCALL -L9 (19): ASSIGN CV1($obj) V5 -L10 (19): RETURN int(1) -LIVE RANGES: - 5: L7 - L9 (new) - -BaseClass::__construct: ; (lines=9, args=1, vars=1, tmps=1) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/28_destructor/28_destructor.php:6-9 -L0 (6): EXT_NOP -L1 (6): CV0($b) = RECV 1 -L2 (7): EXT_STMT -L3 (7): ECHO string("In BaseClass constructor -") -L4 (8): EXT_STMT -L5 (8): ASSIGN_OBJ THIS string("var") -L6 (8): OP_DATA CV0($b) -L7 (9): EXT_STMT -L8 (9): RETURN null - -BaseClass::__destruct: ; (lines=8, args=0, vars=0, tmps=1) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/28_destructor/28_destructor.php:10-13 -L0 (10): EXT_NOP -L1 (11): EXT_STMT -L2 (11): ECHO string("Destroying BaseClass -") -L3 (12): EXT_STMT -L4 (12): T0 = FETCH_OBJ_R THIS string("var") -L5 (12): ECHO T0 -L6 (13): EXT_STMT -L7 (13): RETURN null - + +$_main: + ; (lines=8, args=0, vars=2, tmps=6) + ; (before optimizer) + ; /.../PHP/42_destructor/1_instance_42_destructor/1_instance_42_destructor.php:1-16 + ; return [] RANGE[0..0] +0000 T2 = FETCH_R (global) string("_GET") +0001 T3 = FETCH_DIM_R T2 string("p1") +0002 ASSIGN CV0($b) T3 +0003 V5 = NEW 1 string("BaseClass") +0004 SEND_VAR_EX CV0($b) 1 +0005 DO_FCALL +0006 ASSIGN CV1($obj) V5 +0007 RETURN int(1) +LIVE RANGES: + 5: 0004 - 0006 (new) + +BaseClass::__construct: + ; (lines=4, args=1, vars=1, tmps=1) + ; (before optimizer) + ; /.../PHP/42_destructor/1_instance_42_destructor/1_instance_42_destructor.php:5-7 + ; return [] RANGE[0..0] +0000 CV0($b) = RECV 1 +0001 ASSIGN_OBJ THIS string("var") +0002 OP_DATA CV0($b) +0003 RETURN null + +BaseClass::__destruct: + ; (lines=3, args=0, vars=0, tmps=1) + ; (before optimizer) + ; /.../PHP/42_destructor/1_instance_42_destructor/1_instance_42_destructor.php:8-10 + ; return [] RANGE[0..0] +0000 T0 = FETCH_OBJ_R THIS string("var") +0001 ECHO T0 +0002 RETURN null diff --git a/PHP/42_destructor/1_instance_42_destructor/1_instance_42_destructor.json b/PHP/42_destructor/1_instance_42_destructor/1_instance_42_destructor.json old mode 100644 new mode 100755 index 614eeb7..9310337 --- a/PHP/42_destructor/1_instance_42_destructor/1_instance_42_destructor.json +++ b/PHP/42_destructor/1_instance_42_destructor/1_instance_42_destructor.json @@ -1,37 +1,38 @@ -{ - "code": { - "path": "./1_instance_42_destructor.php", - "injection_skeleton_broken": false - }, - "discovery": { - "rule": "./1_instance_42_destructor.sc", - "method": "joern", - "rule_accuracy": null, - "notes": null - }, - "compile": { - "binary": "./1_instance_42_destructor.bash", - "instruction": null, - "dependencies": null - }, - "expectation": { - "type": "xss", - "sink_file": "./1_instance_42_destructor.php", - "sink_line": 12, - "source_file": "./1_instance_42_destructor.php", - "source_line": 16, - "expectation": true - }, - "properties": { - "category": "S0", - "feature_vs_internal_api": "FEATURE", - "input_sanitizer": false, - "source_and_sink": false, - "negative_test_case": false - }, - "remediation": { - "notes": "", - "transformation": null, - "modeling_rule": null - } +{ + "description": "This instance shows a simple use of `__destruct` and that method contains a sink.", + "code": { + "path": "./1_instance_42_destructor.php", + "injection_skeleton_broken": true + }, + "discovery": { + "rule": "./1_instance_42_destructor.sc", + "method": "joern", + "rule_accuracy": "FP", + "notes": "The discovery rule first gets all class names, where the method `__destruct` is defined.\nAfterwards it collects all `NEW` calles and filters them to see which of the instanciated objects has the method `__destruct` defined.\nThe rule would be perfect, if we could additionally check, if one of the created objects invokes the `__destruct` method within its lifetime." + }, + "compile": { + "binary": "./1_instance_42_destructor.bash", + "instruction": null, + "dependencies": null + }, + "expectation": { + "type": "xss", + "sink_file": "./1_instance_42_destructor.php", + "sink_line": 9, + "source_file": "./1_instance_42_destructor.php", + "source_line": 13, + "expectation": true + }, + "properties": { + "category": "S0", + "feature_vs_internal_api": "FEATURE", + "input_sanitizer": false, + "source_and_sink": false, + "negative_test_case": false + }, + "remediation": { + "notes": "", + "transformation": null, + "modeling_rule": null + } } \ No newline at end of file diff --git a/PHP/42_destructor/1_instance_42_destructor/1_instance_42_destructor.php b/PHP/42_destructor/1_instance_42_destructor/1_instance_42_destructor.php old mode 100644 new mode 100755 index 3f87d4b..363e20c --- a/PHP/42_destructor/1_instance_42_destructor/1_instance_42_destructor.php +++ b/PHP/42_destructor/1_instance_42_destructor/1_instance_42_destructor.php @@ -1,19 +1,16 @@ -var = $b; - } - function __destruct() { - print "Destroying " . __CLASS__ . "\n"; - echo $this->var; - } -} - -$b = $_GET["p1"]; -// In BaseClass constructor -// There is XSS when the object will be destroyed +var = $b; + } + function __destruct() { + echo $this->var; // sink + } +} + +$b = $_GET["p1"]; // source +// In BaseClass constructor +// There is XSS when the object will be destroyed $obj = new BaseClass($b); \ No newline at end of file diff --git a/PHP/42_destructor/1_instance_42_destructor/1_instance_42_destructor.sc b/PHP/42_destructor/1_instance_42_destructor/1_instance_42_destructor.sc old mode 100644 new mode 100755 index a30c204..dce989f --- a/PHP/42_destructor/1_instance_42_destructor/1_instance_42_destructor.sc +++ b/PHP/42_destructor/1_instance_42_destructor/1_instance_42_destructor.sc @@ -1,7 +1,7 @@ -@main def main(name : String): Unit = { - importCpg(name) - def methods8 = cpg.method.name("__destruct").name.l - val x42 = (name, "42_destructor_iall", cpg.call("NEW").argument.filter{x => methods8.contains(x.code.toLowerCase)}.location.toJson); - println(x42) - delete; +@main def main(name : String): Unit = { + importCpg(name) + def classMethods = cpg.method.name("__destruct").astParentFullName.l + val x42 = (name, "42_destructor_iall", cpg.call("NEW").argument.filter{x => classMethods.contains(x.code.toLowerCase)}.location.toJson); + println(x42) + delete; } \ No newline at end of file diff --git a/PHP/42_destructor/42_destructor.json b/PHP/42_destructor/42_destructor.json old mode 100644 new mode 100755 index 0325831..d9193e7 --- a/PHP/42_destructor/42_destructor.json +++ b/PHP/42_destructor/42_destructor.json @@ -1,14 +1,14 @@ -{ - "name": "Destructor", - "description": "", - "family": "code_pattern_php", - "tags": [ - "sast", - "php", - "php_v7.4.9" - ], - "instances": [ - "./1_instance_42_destructor/1_instance_42_destructor.json" - ], - "version": "v0.draft" +{ + "name": "Destructor", + "description": "./docs/description.md", + "family": "code_pattern_php", + "tags": [ + "sast", + "php", + "php_v7.4.9" + ], + "instances": [ + "./1_instance_42_destructor/1_instance_42_destructor.json" + ], + "version": "v1.0" } \ No newline at end of file diff --git a/PHP/42_destructor/Pattern Destructor.md b/PHP/42_destructor/Pattern Destructor.md deleted file mode 100644 index 4b0fcc1..0000000 --- a/PHP/42_destructor/Pattern Destructor.md +++ /dev/null @@ -1,113 +0,0 @@ -# Pattern: Destructor - -## Category - -Objects - -## Definition - -## Instances - -### Instance 1 - -- CATEGORY: S0 -- FEATURE vs INTERNAL API: FEATURE -- INPUT SANITIZERS: NO -- SOURCES AND SINKS: NO -- NEGATIVE TEST CASES: NO -- CODE: - -```php -var = $b; - } - function __destruct() { - print "Destroying " . __CLASS__ . "\n"; - echo $this->var; - } -} - -$b = $_GET["p1"]; -// In BaseClass constructor -// There is XSS when the object will be destroyed -$obj = new BaseClass($b); -``` - -- MEASUREMENT: - -| Tool | RIPS | phpSAFE | WAP | Progpilot | Comm_1 | Comm_2 | Correct | -| ------------- | ---- | ------- | ---- | --------- | ------- | --------- | ------- | -| Vulnerability | NO | NO | NO | YES | NO | NO | YES | -Measurements Date: 8 June 2021 - -- OPCODE: - -```bash -$_main: ; (lines=11, args=0, vars=2, tmps=6) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/28_destructor/28_destructor.php:1-19 -L0 (4): NOP -L1 (16): EXT_STMT -L2 (16): T2 = FETCH_R (global) string("_GET") -L3 (16): T3 = FETCH_DIM_R T2 string("p1") -L4 (16): ASSIGN CV0($b) T3 -L5 (19): EXT_STMT -L6 (19): V5 = NEW 1 string("BaseClass") -L7 (19): SEND_VAR_EX CV0($b) 1 -L8 (19): DO_FCALL -L9 (19): ASSIGN CV1($obj) V5 -L10 (19): RETURN int(1) -LIVE RANGES: - 5: L7 - L9 (new) - -BaseClass::__construct: ; (lines=9, args=1, vars=1, tmps=1) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/28_destructor/28_destructor.php:6-9 -L0 (6): EXT_NOP -L1 (6): CV0($b) = RECV 1 -L2 (7): EXT_STMT -L3 (7): ECHO string("In BaseClass constructor -") -L4 (8): EXT_STMT -L5 (8): ASSIGN_OBJ THIS string("var") -L6 (8): OP_DATA CV0($b) -L7 (9): EXT_STMT -L8 (9): RETURN null - -BaseClass::__destruct: ; (lines=8, args=0, vars=0, tmps=1) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/28_destructor/28_destructor.php:10-13 -L0 (10): EXT_NOP -L1 (11): EXT_STMT -L2 (11): ECHO string("Destroying BaseClass -") -L3 (12): EXT_STMT -L4 (12): T0 = FETCH_OBJ_R THIS string("var") -L5 (12): ECHO T0 -L6 (13): EXT_STMT -L7 (13): RETURN null -``` - -- DISCOVERY: - -```bash -def methods8 = cpg.typeDecl.filter{x => x.method.name.l.contains("__destruct")}.name.l -cpg.call("NEW").argument.filter{x => methods8.contains(x.code.toLowerCase)}.size - -``` - -- PRECONDITIONS: - 1. - -- TRANSFORMATION: - -``` - -``` - diff --git a/PHP/42_destructor/README.md b/PHP/42_destructor/README.md new file mode 100755 index 0000000..4dfd812 --- /dev/null +++ b/PHP/42_destructor/README.md @@ -0,0 +1,133 @@ +[//]: # (This file is automatically generated. If you wish to make any changes, please use the JSON files and regenerate this file using the tpframework.) + +# Destructor + +Tags: sast, php, php_v7.4.9 + +Version: v1.0 + +## Description + +In PHP `__destruct()` will be called as soon as there are no more references pointing to the object or in a shutdown operation. + +## Overview + +| Instances | has discovery rule | discovery method | rule successfull | +|---------------------------|----------------------|--------------------|--------------------| +| [1 Instance](#1-instance) | yes | joern | yes | + +## 1 Instance + +This instance shows a simple use of `__destruct` and that method contains a sink. + +### Code + +```PHP +var = $b; + } + function __destruct() { + echo $this->var; // sink + } +} + +$b = $_GET["p1"]; // source +// In BaseClass constructor +// There is XSS when the object will be destroyed +$obj = new BaseClass($b); +``` + +### Instance Properties + +| category | feature_vs_internal_api | input_sanitizer | negative_test_case | source_and_sink | +|------------|---------------------------|-------------------|----------------------|-------------------| +| S0 | FEATURE | no | no | no | + +
+ +More + +
+ + +### Compile + + +```bash +$_main: + ; (lines=8, args=0, vars=2, tmps=6) + ; (before optimizer) + ; /.../PHP/42_destructor/1_instance_42_destructor/1_instance_42_destructor.php:1-16 + ; return [] RANGE[0..0] +0000 T2 = FETCH_R (global) string("_GET") +0001 T3 = FETCH_DIM_R T2 string("p1") +0002 ASSIGN CV0($b) T3 +0003 V5 = NEW 1 string("BaseClass") +0004 SEND_VAR_EX CV0($b) 1 +0005 DO_FCALL +0006 ASSIGN CV1($obj) V5 +0007 RETURN int(1) +LIVE RANGES: + 5: 0004 - 0006 (new) + +BaseClass::__construct: + ; (lines=4, args=1, vars=1, tmps=1) + ; (before optimizer) + ; /.../PHP/42_destructor/1_instance_42_destructor/1_instance_42_destructor.php:5-7 + ; return [] RANGE[0..0] +0000 CV0($b) = RECV 1 +0001 ASSIGN_OBJ THIS string("var") +0002 OP_DATA CV0($b) +0003 RETURN null + +BaseClass::__destruct: + ; (lines=3, args=0, vars=0, tmps=1) + ; (before optimizer) + ; /.../PHP/42_destructor/1_instance_42_destructor/1_instance_42_destructor.php:8-10 + ; return [] RANGE[0..0] +0000 T0 = FETCH_OBJ_R THIS string("var") +0001 ECHO T0 +0002 RETURN null +``` + +
+ +
+ + +### Discovery + + +The discovery rule first gets all class names, where the method `__destruct` is defined. +Afterwards it collects all `NEW` calles and filters them to see which of the instanciated objects has the method `__destruct` defined. +The rule would be perfect, if we could additionally check, if one of the created objects invokes the `__destruct` method within its lifetime. + +```scala +def classMethods = cpg.method.name("__destruct").astParentFullName.l +val x42 = (name, "42_destructor_iall", cpg.call("NEW").argument.filter{x => classMethods.contains(x.code.toLowerCase)}.location.toJson); +``` + +| discovery method | expected accuracy | +|--------------------|---------------------| +| joern | FP | + +
+ +
+ + +### Measurement + + +| Tool | Comm_1 | Comm_2 | phpSAFE | Progpilot | RIPS | WAP | Ground Truth | +|-------------|----------|----------|-----------|-------------|--------|-------|----------------| +| 08 Jun 2021 | no | no | no | yes | no | no | yes | +| 22 May 2023 | no | no | | | | | yes | + +
+ +
diff --git a/PHP/42_destructor/docs/description.md b/PHP/42_destructor/docs/description.md new file mode 100755 index 0000000..80769db --- /dev/null +++ b/PHP/42_destructor/docs/description.md @@ -0,0 +1 @@ +In PHP `__destruct()` will be called as soon as there are no more references pointing to the object or in a shutdown operation. \ No newline at end of file diff --git a/PHP/43_tostring_echo_object/1_instance_43_tostring_echo_object/1_instance_43_tostring_echo_object.bash b/PHP/43_tostring_echo_object/1_instance_43_tostring_echo_object/1_instance_43_tostring_echo_object.bash old mode 100644 new mode 100755 index d8d01d3..3f4a66a --- a/PHP/43_tostring_echo_object/1_instance_43_tostring_echo_object/1_instance_43_tostring_echo_object.bash +++ b/PHP/43_tostring_echo_object/1_instance_43_tostring_echo_object/1_instance_43_tostring_echo_object.bash @@ -1,40 +1,41 @@ - -$_main: ; (lines=13, args=0, vars=2, tmps=6) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/34_tostring_echo_object/34_tostring_echo_object.php:1-17 -L0 (4): NOP -L1 (14): EXT_STMT -L2 (14): T2 = FETCH_R (global) string("_GET") -L3 (14): T3 = FETCH_DIM_R T2 string("p1") -L4 (14): ASSIGN CV0($b) T3 -L5 (15): EXT_STMT -L6 (15): V5 = NEW 1 string("TestClass") -L7 (15): SEND_VAR_EX CV0($b) 1 -L8 (15): DO_FCALL -L9 (15): ASSIGN CV1($class) V5 -L10 (17): EXT_STMT -L11 (17): ECHO CV1($class) -L12 (17): RETURN int(1) -LIVE RANGES: - 5: L7 - L9 (new) - -TestClass::__construct: ; (lines=7, args=1, vars=1, tmps=1) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/34_tostring_echo_object/34_tostring_echo_object.php:5-7 -L0 (5): EXT_NOP -L1 (5): CV0($foo) = RECV 1 -L2 (6): EXT_STMT -L3 (6): ASSIGN_OBJ THIS string("foo") -L4 (6): OP_DATA CV0($foo) -L5 (7): EXT_STMT -L6 (7): RETURN null - -TestClass::__toString: ; (lines=6, args=0, vars=0, tmps=1) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/34_tostring_echo_object/34_tostring_echo_object.php:9-11 -L0 (9): EXT_NOP -L1 (10): EXT_STMT -L2 (10): T0 = FETCH_OBJ_R THIS string("foo") -L3 (10): RETURN T0 -L4 (11): EXT_STMT -L5 (11): RETURN null + +$_main: + ; (lines=10, args=0, vars=2, tmps=6) + ; (before optimizer) + ; /.../PHP/43_tostring_echo_object/1_instance_43_tostring_echo_object/1_instance_43_tostring_echo_object.php:1-16 + ; return [] RANGE[0..0] +0000 DECLARE_CLASS string("testclass") +0001 T2 = FETCH_R (global) string("_GET") +0002 T3 = FETCH_DIM_R T2 string("p1") +0003 ASSIGN CV0($b) T3 +0004 V5 = NEW 1 string("TestClass") +0005 SEND_VAR_EX CV0($b) 1 +0006 DO_FCALL +0007 ASSIGN CV1($class) V5 +0008 ECHO CV1($class) +0009 RETURN int(1) +LIVE RANGES: + 5: 0005 - 0007 (new) + +TestClass::__construct: + ; (lines=4, args=1, vars=1, tmps=1) + ; (before optimizer) + ; /.../PHP/43_tostring_echo_object/1_instance_43_tostring_echo_object/1_instance_43_tostring_echo_object.php:4-6 + ; return [] RANGE[0..0] +0000 CV0($foo) = RECV 1 +0001 ASSIGN_OBJ THIS string("foo") +0002 OP_DATA CV0($foo) +0003 RETURN null + +TestClass::__toString: + ; (lines=5, args=0, vars=0, tmps=1) + ; (before optimizer) + ; /.../PHP/43_tostring_echo_object/1_instance_43_tostring_echo_object/1_instance_43_tostring_echo_object.php:8-10 + ; return [] RANGE[0..0] +0000 T0 = FETCH_OBJ_R THIS string("foo") +0001 VERIFY_RETURN_TYPE T0 +0002 RETURN T0 +0003 VERIFY_RETURN_TYPE +0004 RETURN null +LIVE RANGES: + 0: 0001 - 0002 (tmp/var) diff --git a/PHP/43_tostring_echo_object/1_instance_43_tostring_echo_object/1_instance_43_tostring_echo_object.json b/PHP/43_tostring_echo_object/1_instance_43_tostring_echo_object/1_instance_43_tostring_echo_object.json old mode 100644 new mode 100755 index e24c382..948d103 --- a/PHP/43_tostring_echo_object/1_instance_43_tostring_echo_object/1_instance_43_tostring_echo_object.json +++ b/PHP/43_tostring_echo_object/1_instance_43_tostring_echo_object/1_instance_43_tostring_echo_object.json @@ -1,37 +1,38 @@ -{ - "code": { - "path": "./1_instance_43_tostring_echo_object.php", - "injection_skeleton_broken": false - }, - "discovery": { - "rule": "./1_instance_43_tostring_echo_object.sc", - "method": "joern", - "rule_accuracy": null, - "notes": null - }, - "compile": { - "binary": "./1_instance_43_tostring_echo_object.bash", - "instruction": null, - "dependencies": null - }, - "expectation": { - "type": "xss", - "sink_file": "./1_instance_43_tostring_echo_object.php", - "sink_line": 17, - "source_file": "./1_instance_43_tostring_echo_object.php", - "source_line": 14, - "expectation": true - }, - "properties": { - "category": "S0", - "feature_vs_internal_api": "FEATURE", - "input_sanitizer": false, - "source_and_sink": false, - "negative_test_case": false - }, - "remediation": { - "notes": "", - "transformation": null, - "modeling_rule": null - } +{ + "description": "The instance shows, that when you call `echo` on an object, the `to_string` method of that object will be called.", + "code": { + "path": "./1_instance_43_tostring_echo_object.php", + "injection_skeleton_broken": false + }, + "discovery": { + "rule": "./1_instance_43_tostring_echo_object.sc", + "method": "joern", + "rule_accuracy": "FP", + "notes": "The discovery rule first gets all class names, where the method `__toString` is defined.\nAfterwards it collects all `NEW` calles and filters them to see which of the instanciated objects has the method `__toString` defined.\nThe rule would be perfect, if we could additionally check, if one of the created objects invokes the `__toString` method within its lifetime." + }, + "compile": { + "binary": "./1_instance_43_tostring_echo_object.bash", + "instruction": null, + "dependencies": null + }, + "expectation": { + "type": "xss", + "sink_file": "./1_instance_43_tostring_echo_object.php", + "sink_line": 16, + "source_file": "./1_instance_43_tostring_echo_object.php", + "source_line": 13, + "expectation": true + }, + "properties": { + "category": "S0", + "feature_vs_internal_api": "FEATURE", + "input_sanitizer": false, + "source_and_sink": false, + "negative_test_case": false + }, + "remediation": { + "notes": "If that is a problem, is it easier for a SAST tool, to call `__toString` manually before passing it to `echo`?", + "transformation": null, + "modeling_rule": null + } } \ No newline at end of file diff --git a/PHP/43_tostring_echo_object/1_instance_43_tostring_echo_object/1_instance_43_tostring_echo_object.php b/PHP/43_tostring_echo_object/1_instance_43_tostring_echo_object/1_instance_43_tostring_echo_object.php old mode 100644 new mode 100755 index 2bb1d21..4669527 --- a/PHP/43_tostring_echo_object/1_instance_43_tostring_echo_object/1_instance_43_tostring_echo_object.php +++ b/PHP/43_tostring_echo_object/1_instance_43_tostring_echo_object/1_instance_43_tostring_echo_object.php @@ -1,17 +1,16 @@ -foo = $foo; - } - - public function __toString(){ - return $this->foo; - } -} - -$b = $_GET["p1"]; -$class = new TestClass($b); -// XSS vulnerability, will print the input $b -echo $class; \ No newline at end of file +foo = $foo; + } + + public function __toString() { + return $this->foo; + } +} + +$b = $_GET["p1"]; // source +$class = new TestClass($b); +// XSS vulnerability, will print the input $b +echo $class; // sink \ No newline at end of file diff --git a/PHP/43_tostring_echo_object/1_instance_43_tostring_echo_object/1_instance_43_tostring_echo_object.sc b/PHP/43_tostring_echo_object/1_instance_43_tostring_echo_object/1_instance_43_tostring_echo_object.sc old mode 100644 new mode 100755 index a0e6241..c943a20 --- a/PHP/43_tostring_echo_object/1_instance_43_tostring_echo_object/1_instance_43_tostring_echo_object.sc +++ b/PHP/43_tostring_echo_object/1_instance_43_tostring_echo_object/1_instance_43_tostring_echo_object.sc @@ -1,7 +1,7 @@ -@main def main(name : String): Unit = { - importCpg(name) - def methods9 = cpg.method.name("__tostring").name.l - val x43 = (name, "43_tostring_echo_object_iall", cpg.call("NEW").argument.filter{x => methods9.contains(x.code.toLowerCase)}.location.toJson); - println(x43) - delete; +@main def main(name : String): Unit = { + importCpg(name) + def classMethods = cpg.method.name("__tostring").astParentFullName.l + val x43 = (name, "43_tostring_echo_object_iall", cpg.call("NEW").argument.filter{x => classMethods.contains(x.code.toLowerCase)}.location.toJson); + println(x43) + delete; } \ No newline at end of file diff --git a/PHP/43_tostring_echo_object/43_tostring_echo_object.json b/PHP/43_tostring_echo_object/43_tostring_echo_object.json old mode 100644 new mode 100755 index 04a4c9a..f9636d8 --- a/PHP/43_tostring_echo_object/43_tostring_echo_object.json +++ b/PHP/43_tostring_echo_object/43_tostring_echo_object.json @@ -1,14 +1,14 @@ -{ - "name": "Tostring Echo Object", - "description": "", - "family": "code_pattern_php", - "tags": [ - "sast", - "php", - "php_v7.4.9" - ], - "instances": [ - "./1_instance_43_tostring_echo_object/1_instance_43_tostring_echo_object.json" - ], - "version": "v0.draft" +{ + "name": "Tostring Echo Object", + "description": "./docs/description.md", + "family": "code_pattern_php", + "tags": [ + "sast", + "php", + "php_v7.4.9" + ], + "instances": [ + "./1_instance_43_tostring_echo_object/1_instance_43_tostring_echo_object.json" + ], + "version": "v1.0" } \ No newline at end of file diff --git a/PHP/43_tostring_echo_object/Pattern Tostring and Echo Object.md b/PHP/43_tostring_echo_object/Pattern Tostring and Echo Object.md deleted file mode 100644 index 0ea4937..0000000 --- a/PHP/43_tostring_echo_object/Pattern Tostring and Echo Object.md +++ /dev/null @@ -1,112 +0,0 @@ -# Pattern: ToString and Echo Object - -## Category - -Objects - -## Definition - -## Instances - -### Instance 1 - -- CATEGORY: S0 -- FEATURE vs INTERNAL API: FEATURE -- INPUT SANITIZERS: NO -- SOURCES AND SINKS: NO -- NEGATIVE TEST CASES: NO -- CODE: - -```php -foo = $foo; - } - - public function __toString(){ - return $this->foo; - } -} - -$b = $_GET["p1"]; -$class = new TestClass($b); -// XSS vulnerability, will print the input $b -echo $class; -``` - -- MEASUREMENT: - -| Tool | RIPS | phpSAFE | WAP | Progpilot | Comm_1 | Comm_2 | Correct | -| ------------- | ---- | ------- | ---- | --------- | ------- | --------- | ------- | -| Vulnerability | NO | NO | NO | NO | YES | YES | YES | -Measurements Date: 8 June 2021 - -- OPCODE: - -```bash -$_main: ; (lines=13, args=0, vars=2, tmps=6) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/34_tostring_echo_object/34_tostring_echo_object.php:1-17 -L0 (4): NOP -L1 (14): EXT_STMT -L2 (14): T2 = FETCH_R (global) string("_GET") -L3 (14): T3 = FETCH_DIM_R T2 string("p1") -L4 (14): ASSIGN CV0($b) T3 -L5 (15): EXT_STMT -L6 (15): V5 = NEW 1 string("TestClass") -L7 (15): SEND_VAR_EX CV0($b) 1 -L8 (15): DO_FCALL -L9 (15): ASSIGN CV1($class) V5 -L10 (17): EXT_STMT -L11 (17): ECHO CV1($class) -L12 (17): RETURN int(1) -LIVE RANGES: - 5: L7 - L9 (new) - -TestClass::__construct: ; (lines=7, args=1, vars=1, tmps=1) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/34_tostring_echo_object/34_tostring_echo_object.php:5-7 -L0 (5): EXT_NOP -L1 (5): CV0($foo) = RECV 1 -L2 (6): EXT_STMT -L3 (6): ASSIGN_OBJ THIS string("foo") -L4 (6): OP_DATA CV0($foo) -L5 (7): EXT_STMT -L6 (7): RETURN null - -TestClass::__toString: ; (lines=6, args=0, vars=0, tmps=1) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/34_tostring_echo_object/34_tostring_echo_object.php:9-11 -L0 (9): EXT_NOP -L1 (10): EXT_STMT -L2 (10): T0 = FETCH_OBJ_R THIS string("foo") -L3 (10): RETURN T0 -L4 (11): EXT_STMT -L5 (11): RETURN null -``` - -- DISCOVERY: - - -```bash -def methods9 = cpg.typeDecl.filter{x => x.method.name.l.contains("__tostring")}.name.l -cpg.call("NEW").argument.filter{x => methods9.contains(x.code.toLowerCase)}.size - -``` - -- PRECONDITIONS: - 1. - -- TRANSFORMATION: - -Call the toString function manually. - -``` - -``` - - - diff --git a/PHP/43_tostring_echo_object/README.md b/PHP/43_tostring_echo_object/README.md new file mode 100755 index 0000000..04c3c2c --- /dev/null +++ b/PHP/43_tostring_echo_object/README.md @@ -0,0 +1,149 @@ +[//]: # (This file is automatically generated. If you wish to make any changes, please use the JSON files and regenerate this file using the tpframework.) + +# Tostring Echo Object + +Tags: sast, php, php_v7.4.9 + +Version: v1.0 + +## Description + +`__toString()` is one of PHP magic functions, that override PHP's default behaviour. When implementing this function you can decide what happens when the object is treated as a string. + +## Overview + +| Instances | has discovery rule | discovery method | rule successfull | +|---------------------------|----------------------|--------------------|--------------------| +| [1 Instance](#1-instance) | yes | joern | yes | + +## 1 Instance + +The instance shows, that when you call `echo` on an object, the `to_string` method of that object will be called. + +### Code + +```PHP +foo = $foo; + } + + public function __toString() { + return $this->foo; + } +} + +$b = $_GET["p1"]; // source +$class = new TestClass($b); +// XSS vulnerability, will print the input $b +echo $class; // sink +``` + +### Instance Properties + +| category | feature_vs_internal_api | input_sanitizer | negative_test_case | source_and_sink | +|------------|---------------------------|-------------------|----------------------|-------------------| +| S0 | FEATURE | no | no | no | + +
+ +More + +
+ + +### Compile + + +```bash +$_main: + ; (lines=10, args=0, vars=2, tmps=6) + ; (before optimizer) + ; /.../PHP/43_tostring_echo_object/1_instance_43_tostring_echo_object/1_instance_43_tostring_echo_object.php:1-16 + ; return [] RANGE[0..0] +0000 DECLARE_CLASS string("testclass") +0001 T2 = FETCH_R (global) string("_GET") +0002 T3 = FETCH_DIM_R T2 string("p1") +0003 ASSIGN CV0($b) T3 +0004 V5 = NEW 1 string("TestClass") +0005 SEND_VAR_EX CV0($b) 1 +0006 DO_FCALL +0007 ASSIGN CV1($class) V5 +0008 ECHO CV1($class) +0009 RETURN int(1) +LIVE RANGES: + 5: 0005 - 0007 (new) + +TestClass::__construct: + ; (lines=4, args=1, vars=1, tmps=1) + ; (before optimizer) + ; /.../PHP/43_tostring_echo_object/1_instance_43_tostring_echo_object/1_instance_43_tostring_echo_object.php:4-6 + ; return [] RANGE[0..0] +0000 CV0($foo) = RECV 1 +0001 ASSIGN_OBJ THIS string("foo") +0002 OP_DATA CV0($foo) +0003 RETURN null + +TestClass::__toString: + ; (lines=5, args=0, vars=0, tmps=1) + ; (before optimizer) + ; /.../PHP/43_tostring_echo_object/1_instance_43_tostring_echo_object/1_instance_43_tostring_echo_object.php:8-10 + ; return [] RANGE[0..0] +0000 T0 = FETCH_OBJ_R THIS string("foo") +0001 VERIFY_RETURN_TYPE T0 +0002 RETURN T0 +0003 VERIFY_RETURN_TYPE +0004 RETURN null +LIVE RANGES: + 0: 0001 - 0002 (tmp/var) +``` + +
+ +
+ + +### Discovery + + +The discovery rule first gets all class names, where the method `__toString` is defined. +Afterwards it collects all `NEW` calles and filters them to see which of the instanciated objects has the method `__toString` defined. +The rule would be perfect, if we could additionally check, if one of the created objects invokes the `__toString` method within its lifetime. + +```scala +def classMethods = cpg.method.name("__tostring").astParentFullName.l +val x43 = (name, "43_tostring_echo_object_iall", cpg.call("NEW").argument.filter{x => classMethods.contains(x.code.toLowerCase)}.location.toJson); +``` + +| discovery method | expected accuracy | +|--------------------|---------------------| +| joern | FP | + +
+ +
+ + +### Measurement + + +| Tool | Comm_1 | Comm_2 | phpSAFE | Progpilot | RIPS | WAP | Ground Truth | +|-------------|----------|----------|-----------|-------------|--------|-------|----------------| +| 08 Jun 2021 | yes | yes | no | no | no | no | yes | +| 22 May 2023 | yes | yes | | | | | yes | + +
+ +
+ + +### Remediation + + +If that is a problem, is it easier for a SAST tool, to call `__toString` manually before passing it to `echo`? + +
+ +
diff --git a/PHP/43_tostring_echo_object/docs/description.md b/PHP/43_tostring_echo_object/docs/description.md new file mode 100755 index 0000000..914ab61 --- /dev/null +++ b/PHP/43_tostring_echo_object/docs/description.md @@ -0,0 +1 @@ +`__toString()` is one of PHP magic functions, that override PHP's default behaviour. When implementing this function you can decide what happens when the object is treated as a string. \ No newline at end of file diff --git a/PHP/44_verify_return_type/1_instance_44_verify_return_type/1_instance_44_verify_return_type.bash b/PHP/44_verify_return_type/1_instance_44_verify_return_type/1_instance_44_verify_return_type.bash old mode 100644 new mode 100755 index 4d4ef2a..312e2fe --- a/PHP/44_verify_return_type/1_instance_44_verify_return_type/1_instance_44_verify_return_type.bash +++ b/PHP/44_verify_return_type/1_instance_44_verify_return_type/1_instance_44_verify_return_type.bash @@ -1,57 +1,54 @@ - -$_main: ; (lines=16, args=0, vars=2, tmps=7) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/85_verify_return_type/first_ex/first_ex.php:1-23 -L0 (4): NOP -L1 (5): NOP -L2 (21): EXT_STMT -L3 (21): T2 = FETCH_R (global) string("_GET") -L4 (21): T3 = FETCH_DIM_R T2 string("p1") -L5 (21): ASSIGN CV0($b) T3 -L6 (22): EXT_STMT -L7 (22): V5 = NEW 1 string("TestClass") -L8 (22): SEND_VAR_EX CV0($b) 1 -L9 (22): DO_FCALL -L10 (22): ASSIGN CV1($c) V5 -L11 (23): EXT_STMT -L12 (23): INIT_FCALL 1 96 string("f") -L13 (23): SEND_VAR CV1($c) 1 -L14 (23): DO_FCALL -L15 (23): RETURN int(1) -LIVE RANGES: - 5: L8 - L10 (new) - -F: ; (lines=6, args=1, vars=1, tmps=0) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/85_verify_return_type/first_ex/first_ex.php:17-19 -L0 (17): EXT_NOP -L1 (17): CV0($c) = RECV 1 -L2 (18): EXT_STMT -L3 (18): ECHO string("message") -L4 (19): EXT_STMT -L5 (19): RETURN null - -TestClass::__construct: ; (lines=7, args=1, vars=1, tmps=1) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/85_verify_return_type/first_ex/first_ex.php:6-8 -L0 (6): EXT_NOP -L1 (6): CV0($foo) = RECV 1 -L2 (7): EXT_STMT -L3 (7): ASSIGN_OBJ THIS string("foo") -L4 (7): OP_DATA CV0($foo) -L5 (8): EXT_STMT -L6 (8): RETURN null - -TestClass::__toString: ; (lines=10, args=0, vars=0, tmps=3) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/85_verify_return_type/first_ex/first_ex.php:10-13 -L0 (10): EXT_NOP -L1 (11): EXT_STMT -L2 (11): T0 = FETCH_OBJ_R THIS string("foo") -L3 (11): T1 = CONCAT string("XSS: ") T0 -L4 (11): ECHO T1 -L5 (12): EXT_STMT -L6 (12): T2 = FETCH_OBJ_R THIS string("foo") -L7 (12): RETURN T2 -L8 (13): EXT_STMT -L9 (13): RETURN null + +$_main: + ; (lines=12, args=0, vars=2, tmps=7) + ; (before optimizer) + ; /.../PHP/44_verify_return_type/1_instance_44_verify_return_type/1_instance_44_verify_return_type.php:1-21 + ; return [] RANGE[0..0] +0000 DECLARE_CLASS string("testclass") +0001 T2 = FETCH_R (global) string("_GET") +0002 T3 = FETCH_DIM_R T2 string("p1") +0003 ASSIGN CV0($b) T3 +0004 V5 = NEW 1 string("TestClass") +0005 SEND_VAR_EX CV0($b) 1 +0006 DO_FCALL +0007 ASSIGN CV1($c) V5 +0008 INIT_FCALL 1 96 string("f") +0009 SEND_VAR CV1($c) 1 +0010 DO_UCALL +0011 RETURN int(1) +LIVE RANGES: + 5: 0005 - 0007 (new) + +F: + ; (lines=3, args=1, vars=1, tmps=0) + ; (before optimizer) + ; /.../PHP/44_verify_return_type/1_instance_44_verify_return_type/1_instance_44_verify_return_type.php:15-17 + ; return [] RANGE[0..0] +0000 CV0($c) = RECV 1 +0001 ECHO string("message") +0002 RETURN null + +TestClass::__construct: + ; (lines=4, args=1, vars=1, tmps=1) + ; (before optimizer) + ; /.../PHP/44_verify_return_type/1_instance_44_verify_return_type/1_instance_44_verify_return_type.php:4-6 + ; return [] RANGE[0..0] +0000 CV0($val) = RECV 1 +0001 ASSIGN_OBJ THIS string("x") +0002 OP_DATA CV0($val) +0003 RETURN null + +TestClass::__toString: + ; (lines=7, args=0, vars=0, tmps=2) + ; (before optimizer) + ; /.../PHP/44_verify_return_type/1_instance_44_verify_return_type/1_instance_44_verify_return_type.php:8-11 + ; return [] RANGE[0..0] +0000 T0 = FETCH_OBJ_R THIS string("x") +0001 ECHO T0 +0002 T1 = FETCH_OBJ_R THIS string("x") +0003 VERIFY_RETURN_TYPE T1 +0004 RETURN T1 +0005 VERIFY_RETURN_TYPE +0006 RETURN null +LIVE RANGES: + 1: 0003 - 0004 (tmp/var) diff --git a/PHP/44_verify_return_type/1_instance_44_verify_return_type/1_instance_44_verify_return_type.json b/PHP/44_verify_return_type/1_instance_44_verify_return_type/1_instance_44_verify_return_type.json old mode 100644 new mode 100755 index 4d1e591..78eb291 --- a/PHP/44_verify_return_type/1_instance_44_verify_return_type/1_instance_44_verify_return_type.json +++ b/PHP/44_verify_return_type/1_instance_44_verify_return_type/1_instance_44_verify_return_type.json @@ -1,37 +1,38 @@ -{ - "code": { - "path": "./1_instance_44_verify_return_type.php", - "injection_skeleton_broken": false - }, - "discovery": { - "rule": "../44_verify_return_type.sc", - "method": "joern", - "rule_accuracy": null, - "notes": null - }, - "compile": { - "binary": "./1_instance_44_verify_return_type.bash", - "instruction": null, - "dependencies": null - }, - "expectation": { - "type": "xss", - "sink_file": "./1_instance_44_verify_return_type.php", - "sink_line": 11, - "source_file": "./1_instance_44_verify_return_type.php", - "source_line": 21, - "expectation": true - }, - "properties": { - "category": "S0", - "feature_vs_internal_api": "FEATURE", - "input_sanitizer": false, - "source_and_sink": false, - "negative_test_case": false - }, - "remediation": { - "notes": "", - "transformation": null, - "modeling_rule": null - } +{ + "description": "The instance defines a function, that takes a string as an argument. So if the argument is an object, `__toString` will be called on that object.", + "code": { + "path": "./1_instance_44_verify_return_type.php", + "injection_skeleton_broken": true + }, + "discovery": { + "rule": "./1_instance_44_verify_return_type.sc", + "method": "joern", + "rule_accuracy": "FP", + "notes": "The discovery rule first gets all class names, where the method `__toString` is defined.\nAfterwards it collects all `NEW` calles and filters them to see which of the instanciated objects has the method `__toString` defined.\nThe rule would be perfect, if we could additionally check, if one of the created objects invokes the `__toString` method within its lifetime." + }, + "compile": { + "binary": "./1_instance_44_verify_return_type.bash", + "instruction": null, + "dependencies": null + }, + "expectation": { + "type": "xss", + "sink_file": "./1_instance_44_verify_return_type.php", + "sink_line": 9, + "source_file": "./1_instance_44_verify_return_type.php", + "source_line": 19, + "expectation": true + }, + "properties": { + "category": "S0", + "feature_vs_internal_api": "FEATURE", + "input_sanitizer": false, + "source_and_sink": false, + "negative_test_case": false + }, + "remediation": { + "notes": "Call `__toString` manually on the object, when it is passed to a function, that takes strings as arguments.", + "transformation": null, + "modeling_rule": null + } } \ No newline at end of file diff --git a/PHP/44_verify_return_type/1_instance_44_verify_return_type/1_instance_44_verify_return_type.php b/PHP/44_verify_return_type/1_instance_44_verify_return_type/1_instance_44_verify_return_type.php old mode 100644 new mode 100755 index acacd09..51f034a --- a/PHP/44_verify_return_type/1_instance_44_verify_return_type/1_instance_44_verify_return_type.php +++ b/PHP/44_verify_return_type/1_instance_44_verify_return_type/1_instance_44_verify_return_type.php @@ -1,23 +1,21 @@ -foo = $foo; - } - - public function __toString(){ - echo 'XSS: ' . $this->foo; - return $this->foo; - } -} - -// will call the function __toString(), XSS vulnerability -function F(string $c){ - echo 'message'; -} - -$b = $_GET["p1"]; -$c = new TestClass($b); +x = $val; + } + + public function __toString() { + echo $this->x; // sink + return $this->x; + } +} + +// will call the function __toString(), XSS vulnerability +function F(string $c) { + echo 'message'; +} + +$b = $_GET["p1"]; // source +$c = new TestClass($b); F($c); \ No newline at end of file diff --git a/PHP/44_verify_return_type/1_instance_44_verify_return_type/1_instance_44_verify_return_type.sc b/PHP/44_verify_return_type/1_instance_44_verify_return_type/1_instance_44_verify_return_type.sc old mode 100644 new mode 100755 index 408c774..01feddc --- a/PHP/44_verify_return_type/1_instance_44_verify_return_type/1_instance_44_verify_return_type.sc +++ b/PHP/44_verify_return_type/1_instance_44_verify_return_type/1_instance_44_verify_return_type.sc @@ -1,6 +1,7 @@ -@main def main(name : String): Unit = { - importCpg(name) - val x44 = (name, "44_verify_return_type_iall", cpg.call(".*VERIFY_RETURN_TYPE.*").location.toJson); - println(x44) - delete; -} \ No newline at end of file +@main def main(name : String): Unit = { + importCpg(name) + def methodClasses = cpg.method.name("__tostring").astParentFullName.l + val x44 = (name, "44_verify_return_type_i1", cpg.call("NEW").argument.filter{x => methodClasses.contains(x.code.toLowerCase)}.location.toJson); + println(x44) + delete; +} \ No newline at end of file diff --git a/PHP/44_verify_return_type/2_instance_44_verify_return_type/2_instance_44_verify_return_type.bash b/PHP/44_verify_return_type/2_instance_44_verify_return_type/2_instance_44_verify_return_type.bash old mode 100644 new mode 100755 index 090da09..b0a9264 --- a/PHP/44_verify_return_type/2_instance_44_verify_return_type/2_instance_44_verify_return_type.bash +++ b/PHP/44_verify_return_type/2_instance_44_verify_return_type/2_instance_44_verify_return_type.bash @@ -1,58 +1,56 @@ - -$_main: ; (lines=10, args=0, vars=1, tmps=4) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/85_verify_return_type/second_ex/second_ex.php:1-22 -L0 (4): NOP -L1 (20): EXT_STMT -L2 (20): T1 = FETCH_R (global) string("_GET") -L3 (20): T2 = FETCH_DIM_R T1 string("p1") -L4 (20): ASSIGN CV0($b) T2 -L5 (22): EXT_STMT -L6 (22): INIT_FCALL 1 160 string("f") -L7 (22): SEND_VAR CV0($b) 1 -L8 (22): DO_FCALL -L9 (22): RETURN int(1) - -F: ; (lines=13, args=1, vars=2, tmps=3) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/85_verify_return_type/second_ex/second_ex.php:16-19 -L0 (16): EXT_NOP -L1 (16): CV0($b) = RECV 1 -L2 (17): EXT_STMT -L3 (17): V2 = NEW 1 string("TestClass") -L4 (17): SEND_VAR_EX CV0($b) 1 -L5 (17): DO_FCALL -L6 (17): ASSIGN CV1($class) V2 -L7 (18): EXT_STMT -L8 (18): VERIFY_RETURN_TYPE CV1($class) -L9 (18): RETURN CV1($class) -L10 (19): EXT_STMT -L11 (19): VERIFY_RETURN_TYPE -L12 (19): RETURN null -LIVE RANGES: - 2: L4 - L6 (new) - -TestClass::__construct: ; (lines=7, args=1, vars=1, tmps=1) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/85_verify_return_type/second_ex/second_ex.php:5-7 -L0 (5): EXT_NOP -L1 (5): CV0($foo) = RECV 1 -L2 (6): EXT_STMT -L3 (6): ASSIGN_OBJ THIS string("foo") -L4 (6): OP_DATA CV0($foo) -L5 (7): EXT_STMT -L6 (7): RETURN null - -TestClass::__toString: ; (lines=10, args=0, vars=0, tmps=3) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/85_verify_return_type/second_ex/second_ex.php:9-12 -L0 (9): EXT_NOP -L1 (10): EXT_STMT -L2 (10): T0 = FETCH_OBJ_R THIS string("foo") -L3 (10): T1 = CONCAT string("XSS: ") T0 -L4 (10): ECHO T1 -L5 (11): EXT_STMT -L6 (11): T2 = FETCH_OBJ_R THIS string("foo") -L7 (11): RETURN T2 -L8 (12): EXT_STMT -L9 (12): RETURN null + +$_main: + ; (lines=8, args=0, vars=1, tmps=4) + ; (before optimizer) + ; /.../PHP/44_verify_return_type/2_instance_44_verify_return_type/2_instance_44_verify_return_type.php:1-20 + ; return [] RANGE[0..0] +0000 DECLARE_CLASS string("testclass") +0001 T1 = FETCH_R (global) string("_GET") +0002 T2 = FETCH_DIM_R T1 string("p1") +0003 ASSIGN CV0($b) T2 +0004 INIT_FCALL 1 160 string("f") +0005 SEND_VAR CV0($b) 1 +0006 DO_UCALL +0007 RETURN int(1) + +F: + ; (lines=9, args=1, vars=2, tmps=3) + ; (before optimizer) + ; /.../PHP/44_verify_return_type/2_instance_44_verify_return_type/2_instance_44_verify_return_type.php:15-18 + ; return [] RANGE[0..0] +0000 CV0($b) = RECV 1 +0001 V2 = NEW 1 string("TestClass") +0002 SEND_VAR_EX CV0($b) 1 +0003 DO_FCALL +0004 ASSIGN CV1($class) V2 +0005 VERIFY_RETURN_TYPE CV1($class) +0006 RETURN CV1($class) +0007 VERIFY_RETURN_TYPE +0008 RETURN null +LIVE RANGES: + 2: 0002 - 0004 (new) + +TestClass::__construct: + ; (lines=4, args=1, vars=1, tmps=1) + ; (before optimizer) + ; /.../PHP/44_verify_return_type/2_instance_44_verify_return_type/2_instance_44_verify_return_type.php:4-6 + ; return [] RANGE[0..0] +0000 CV0($val) = RECV 1 +0001 ASSIGN_OBJ THIS string("x") +0002 OP_DATA CV0($val) +0003 RETURN null + +TestClass::__toString: + ; (lines=7, args=0, vars=0, tmps=2) + ; (before optimizer) + ; /.../PHP/44_verify_return_type/2_instance_44_verify_return_type/2_instance_44_verify_return_type.php:8-11 + ; return [] RANGE[0..0] +0000 T0 = FETCH_OBJ_R THIS string("x") +0001 ECHO T0 +0002 T1 = FETCH_OBJ_R THIS string("x") +0003 VERIFY_RETURN_TYPE T1 +0004 RETURN T1 +0005 VERIFY_RETURN_TYPE +0006 RETURN null +LIVE RANGES: + 1: 0003 - 0004 (tmp/var) diff --git a/PHP/44_verify_return_type/2_instance_44_verify_return_type/2_instance_44_verify_return_type.json b/PHP/44_verify_return_type/2_instance_44_verify_return_type/2_instance_44_verify_return_type.json old mode 100644 new mode 100755 index fcbc51c..af0fd42 --- a/PHP/44_verify_return_type/2_instance_44_verify_return_type/2_instance_44_verify_return_type.json +++ b/PHP/44_verify_return_type/2_instance_44_verify_return_type/2_instance_44_verify_return_type.json @@ -1,37 +1,38 @@ -{ - "code": { - "path": "./2_instance_44_verify_return_type.php", - "injection_skeleton_broken": false - }, - "discovery": { - "rule": "../44_verify_return_type.sc", - "method": "joern", - "rule_accuracy": null, - "notes": null - }, - "compile": { - "binary": "./2_instance_44_verify_return_type.bash", - "instruction": null, - "dependencies": null - }, - "expectation": { - "type": "xss", - "sink_file": "./2_instance_44_verify_return_type.php", - "sink_line": 10, - "source_file": "./2_instance_44_verify_return_type.php", - "source_line": 20, - "expectation": true - }, - "properties": { - "category": "S0", - "feature_vs_internal_api": "FEATURE", - "input_sanitizer": false, - "source_and_sink": false, - "negative_test_case": false - }, - "remediation": { - "notes": "", - "transformation": null, - "modeling_rule": null - } +{ + "description": "This instance shows, a function, that returns a string. When returning an object from that function, the `__toString` method of that object is invoked.", + "code": { + "path": "./2_instance_44_verify_return_type.php", + "injection_skeleton_broken": false + }, + "discovery": { + "rule": "./2_instance_44_verify_return_type.sc", + "method": "joern", + "rule_accuracy": "Perfect", + "notes": "That rule searches for `VERIFY_RETURN_TYPE` in opcode and will find all occurences, where any function has an explicit return type." + }, + "compile": { + "binary": "./2_instance_44_verify_return_type.bash", + "instruction": null, + "dependencies": null + }, + "expectation": { + "type": "xss", + "sink_file": "./2_instance_44_verify_return_type.php", + "sink_line": 9, + "source_file": "./2_instance_44_verify_return_type.php", + "source_line": 19, + "expectation": true + }, + "properties": { + "category": "S0", + "feature_vs_internal_api": "FEATURE", + "input_sanitizer": false, + "source_and_sink": false, + "negative_test_case": false + }, + "remediation": { + "notes": "The function returning a specific type could call the conversion to that type explicitly. That might not only include calling `__toString`, but also other type conversions.", + "transformation": null, + "modeling_rule": null + } } \ No newline at end of file diff --git a/PHP/44_verify_return_type/2_instance_44_verify_return_type/2_instance_44_verify_return_type.php b/PHP/44_verify_return_type/2_instance_44_verify_return_type/2_instance_44_verify_return_type.php old mode 100644 new mode 100755 index fd79425..e32bd07 --- a/PHP/44_verify_return_type/2_instance_44_verify_return_type/2_instance_44_verify_return_type.php +++ b/PHP/44_verify_return_type/2_instance_44_verify_return_type/2_instance_44_verify_return_type.php @@ -1,22 +1,20 @@ -foo = $foo; - } - - public function __toString(){ - echo 'XSS: ' . $this->foo; - return $this->foo; - } -} - -// will call the function __toString(), XSS vulnerability -function F($b):string{ - $class = new TestClass($b); - return $class; -} -$b = $_GET["p1"]; -// will print string +x = $val; + } + + public function __toString() { + echo $this->x; // sink + return $this->x; + } +} + +// will call the function __toString(), XSS vulnerability +function F($b):string { + $class = new TestClass($b); + return $class; +} +$b = $_GET["p1"]; // source F($b); \ No newline at end of file diff --git a/PHP/44_verify_return_type/2_instance_44_verify_return_type/2_instance_44_verify_return_type.sc b/PHP/44_verify_return_type/2_instance_44_verify_return_type/2_instance_44_verify_return_type.sc old mode 100644 new mode 100755 index 408c774..9625869 --- a/PHP/44_verify_return_type/2_instance_44_verify_return_type/2_instance_44_verify_return_type.sc +++ b/PHP/44_verify_return_type/2_instance_44_verify_return_type/2_instance_44_verify_return_type.sc @@ -1,6 +1,6 @@ -@main def main(name : String): Unit = { - importCpg(name) - val x44 = (name, "44_verify_return_type_iall", cpg.call(".*VERIFY_RETURN_TYPE.*").location.toJson); - println(x44) - delete; +@main def main(name : String): Unit = { + importCpg(name) + val x44 = (name, "44_verify_return_type_i2", cpg.call(".*VERIFY_RETURN_TYPE.*").location.toJson); + println(x44) + delete; } \ No newline at end of file diff --git a/PHP/44_verify_return_type/44_verify_return_type.json b/PHP/44_verify_return_type/44_verify_return_type.json old mode 100644 new mode 100755 index 9ce63e0..74e9df9 --- a/PHP/44_verify_return_type/44_verify_return_type.json +++ b/PHP/44_verify_return_type/44_verify_return_type.json @@ -1,15 +1,15 @@ -{ - "name": "Verify Return Type", - "description": "", - "family": "code_pattern_php", - "tags": [ - "sast", - "php", - "php_v7.4.9" - ], - "instances": [ - "./1_instance_44_verify_return_type/1_instance_44_verify_return_type.json", - "./2_instance_44_verify_return_type/2_instance_44_verify_return_type.json" - ], - "version": "v0.draft" +{ + "name": "Verify Return Type", + "description": "./docs/description.md", + "family": "code_pattern_php", + "tags": [ + "sast", + "php", + "php_v7.4.9" + ], + "instances": [ + "./1_instance_44_verify_return_type/1_instance_44_verify_return_type.json", + "./2_instance_44_verify_return_type/2_instance_44_verify_return_type.json" + ], + "version": "v1.0" } \ No newline at end of file diff --git a/PHP/44_verify_return_type/44_verify_return_type.sc b/PHP/44_verify_return_type/44_verify_return_type.sc deleted file mode 100644 index 408c774..0000000 --- a/PHP/44_verify_return_type/44_verify_return_type.sc +++ /dev/null @@ -1,6 +0,0 @@ -@main def main(name : String): Unit = { - importCpg(name) - val x44 = (name, "44_verify_return_type_iall", cpg.call(".*VERIFY_RETURN_TYPE.*").location.toJson); - println(x44) - delete; -} \ No newline at end of file diff --git a/PHP/44_verify_return_type/Pattern Verify Return Type.md b/PHP/44_verify_return_type/Pattern Verify Return Type.md deleted file mode 100644 index acddded..0000000 --- a/PHP/44_verify_return_type/Pattern Verify Return Type.md +++ /dev/null @@ -1,250 +0,0 @@ -# Pattern: Verify Return Type - -## Category - -Objects - -## Definition - -## Instances - -### Instance 1 - -- CATEGORY: S0 -- FEATURE vs INTERNAL API: FEATURE -- INPUT SANITIZERS: NO -- SOURCES AND SINKS: NO -- NEGATIVE TEST CASES: NO -- CODE: - -```php -foo = $foo; - } - - public function __toString(){ - echo 'XSS: ' . $this->foo; - return $this->foo; - } -} - -// will call the function __toString(), XSS vulnerability -function F(string $c){ - echo 'message'; -} - -$b = $_GET["p1"]; -$c = new TestClass($b); -F($c); -``` - -- MEASUREMENT: - -| Tool | RIPS | phpSAFE | WAP | Progpilot | Comm_1 | Comm_2 | Correct | -| ------------- | ---- | ------- | ---- | --------- | ------- | --------- | ------- | -| Vulnerability | NO | NO | NO | YES | YES | NO | YES | -Measurements Date: 8 June 2021 - -- OPCODE: - -```bash -$_main: ; (lines=16, args=0, vars=2, tmps=7) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/85_verify_return_type/first_ex/first_ex.php:1-23 -L0 (4): NOP -L1 (5): NOP -L2 (21): EXT_STMT -L3 (21): T2 = FETCH_R (global) string("_GET") -L4 (21): T3 = FETCH_DIM_R T2 string("p1") -L5 (21): ASSIGN CV0($b) T3 -L6 (22): EXT_STMT -L7 (22): V5 = NEW 1 string("TestClass") -L8 (22): SEND_VAR_EX CV0($b) 1 -L9 (22): DO_FCALL -L10 (22): ASSIGN CV1($c) V5 -L11 (23): EXT_STMT -L12 (23): INIT_FCALL 1 96 string("f") -L13 (23): SEND_VAR CV1($c) 1 -L14 (23): DO_FCALL -L15 (23): RETURN int(1) -LIVE RANGES: - 5: L8 - L10 (new) - -F: ; (lines=6, args=1, vars=1, tmps=0) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/85_verify_return_type/first_ex/first_ex.php:17-19 -L0 (17): EXT_NOP -L1 (17): CV0($c) = RECV 1 -L2 (18): EXT_STMT -L3 (18): ECHO string("message") -L4 (19): EXT_STMT -L5 (19): RETURN null - -TestClass::__construct: ; (lines=7, args=1, vars=1, tmps=1) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/85_verify_return_type/first_ex/first_ex.php:6-8 -L0 (6): EXT_NOP -L1 (6): CV0($foo) = RECV 1 -L2 (7): EXT_STMT -L3 (7): ASSIGN_OBJ THIS string("foo") -L4 (7): OP_DATA CV0($foo) -L5 (8): EXT_STMT -L6 (8): RETURN null - -TestClass::__toString: ; (lines=10, args=0, vars=0, tmps=3) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/85_verify_return_type/first_ex/first_ex.php:10-13 -L0 (10): EXT_NOP -L1 (11): EXT_STMT -L2 (11): T0 = FETCH_OBJ_R THIS string("foo") -L3 (11): T1 = CONCAT string("XSS: ") T0 -L4 (11): ECHO T1 -L5 (12): EXT_STMT -L6 (12): T2 = FETCH_OBJ_R THIS string("foo") -L7 (12): RETURN T2 -L8 (13): EXT_STMT -L9 (13): RETURN null -``` - -- DISCOVERY: - -```bash -cpg.call(".*VERIFY_RETURN_TYPE.*").location.l -``` - -- PRECONDITIONS: - 1. - -- TRANSFORMATION: - -Call the toString function manually. - -``` - -``` - -### Instance 2 - -- CATEGORY: S0 -- FEATURE vs INTERNAL API: FEATURE -- INPUT SANITIZERS: NO -- SOURCES AND SINKS: NO -- NEGATIVE TEST CASES: NO -- CODE: - -```php -foo = $foo; - } - - public function __toString(){ - echo 'XSS: ' . $this->foo; - return $this->foo; - } -} - -// will call the function __toString(), XSS vulnerability -function F($b):string{ - $class = new TestClass($b); - return $class; -} -$b = $_GET["p1"]; -// will print string -F($b); -``` - -- MEASUREMENT: - -| Tool | RIPS | phpSAFE | WAP | Progpilot | Comm_1 | Comm_2 | Correct | -| ------------- | ---- | ------- | ---- | --------- | ------- | --------- | ------- | -| Vulnerability | NO | NO | NO | YES | YES | NO | YES | -Measurements Date: 8 June 2021 - -- OPCODE: - -```bash -$_main: ; (lines=10, args=0, vars=1, tmps=4) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/85_verify_return_type/second_ex/second_ex.php:1-22 -L0 (4): NOP -L1 (20): EXT_STMT -L2 (20): T1 = FETCH_R (global) string("_GET") -L3 (20): T2 = FETCH_DIM_R T1 string("p1") -L4 (20): ASSIGN CV0($b) T2 -L5 (22): EXT_STMT -L6 (22): INIT_FCALL 1 160 string("f") -L7 (22): SEND_VAR CV0($b) 1 -L8 (22): DO_FCALL -L9 (22): RETURN int(1) - -F: ; (lines=13, args=1, vars=2, tmps=3) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/85_verify_return_type/second_ex/second_ex.php:16-19 -L0 (16): EXT_NOP -L1 (16): CV0($b) = RECV 1 -L2 (17): EXT_STMT -L3 (17): V2 = NEW 1 string("TestClass") -L4 (17): SEND_VAR_EX CV0($b) 1 -L5 (17): DO_FCALL -L6 (17): ASSIGN CV1($class) V2 -L7 (18): EXT_STMT -L8 (18): VERIFY_RETURN_TYPE CV1($class) -L9 (18): RETURN CV1($class) -L10 (19): EXT_STMT -L11 (19): VERIFY_RETURN_TYPE -L12 (19): RETURN null -LIVE RANGES: - 2: L4 - L6 (new) - -TestClass::__construct: ; (lines=7, args=1, vars=1, tmps=1) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/85_verify_return_type/second_ex/second_ex.php:5-7 -L0 (5): EXT_NOP -L1 (5): CV0($foo) = RECV 1 -L2 (6): EXT_STMT -L3 (6): ASSIGN_OBJ THIS string("foo") -L4 (6): OP_DATA CV0($foo) -L5 (7): EXT_STMT -L6 (7): RETURN null - -TestClass::__toString: ; (lines=10, args=0, vars=0, tmps=3) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/85_verify_return_type/second_ex/second_ex.php:9-12 -L0 (9): EXT_NOP -L1 (10): EXT_STMT -L2 (10): T0 = FETCH_OBJ_R THIS string("foo") -L3 (10): T1 = CONCAT string("XSS: ") T0 -L4 (10): ECHO T1 -L5 (11): EXT_STMT -L6 (11): T2 = FETCH_OBJ_R THIS string("foo") -L7 (11): RETURN T2 -L8 (12): EXT_STMT -L9 (12): RETURN null -``` - -- DISCOVERY: - -```bash -cpg.call(".*VERIFY_RETURN_TYPE.*").location.l -``` - -- PRECONDITIONS: - 1. - -- TRANSFORMATION: - -Call the toString function manually. - -```php - -``` - diff --git a/PHP/44_verify_return_type/README.md b/PHP/44_verify_return_type/README.md new file mode 100755 index 0000000..1ad76a7 --- /dev/null +++ b/PHP/44_verify_return_type/README.md @@ -0,0 +1,328 @@ +[//]: # (This file is automatically generated. If you wish to make any changes, please use the JSON files and regenerate this file using the tpframework.) + +# Verify Return Type + +Tags: sast, php, php_v7.4.9 + +Version: v1.0 + +## Description + +Type declarations can be added in PHP to ensure, the arguments or the return value has the right type. If not, a `TypeError` will be thrown. If an argument or a return value does not have the right type, it will implicitly converted to that type, if possible. + +## Overview + +| Instances | has discovery rule | discovery method | rule successfull | +|---------------------------|----------------------|--------------------|--------------------| +| [1 Instance](#1-instance) | yes | joern | yes | +| [2 Instance](#2-instance) | yes | joern | yes | + +
+ + +## 1 Instance + + +The instance defines a function, that takes a string as an argument. So if the argument is an object, `__toString` will be called on that object. + +### Code + +```PHP +x = $val; + } + + public function __toString() { + echo $this->x; // sink + return $this->x; + } +} + +// will call the function __toString(), XSS vulnerability +function F(string $c) { + echo 'message'; +} + +$b = $_GET["p1"]; // source +$c = new TestClass($b); +F($c); +``` + +### Instance Properties + +| category | feature_vs_internal_api | input_sanitizer | negative_test_case | source_and_sink | +|------------|---------------------------|-------------------|----------------------|-------------------| +| S0 | FEATURE | no | no | no | + +
+ +More + +
+ + +### Compile + + +```bash +$_main: + ; (lines=12, args=0, vars=2, tmps=7) + ; (before optimizer) + ; /.../PHP/44_verify_return_type/1_instance_44_verify_return_type/1_instance_44_verify_return_type.php:1-21 + ; return [] RANGE[0..0] +0000 DECLARE_CLASS string("testclass") +0001 T2 = FETCH_R (global) string("_GET") +0002 T3 = FETCH_DIM_R T2 string("p1") +0003 ASSIGN CV0($b) T3 +0004 V5 = NEW 1 string("TestClass") +0005 SEND_VAR_EX CV0($b) 1 +0006 DO_FCALL +0007 ASSIGN CV1($c) V5 +0008 INIT_FCALL 1 96 string("f") +0009 SEND_VAR CV1($c) 1 +0010 DO_UCALL +0011 RETURN int(1) +LIVE RANGES: + 5: 0005 - 0007 (new) + +F: + ; (lines=3, args=1, vars=1, tmps=0) + ; (before optimizer) + ; /.../PHP/44_verify_return_type/1_instance_44_verify_return_type/1_instance_44_verify_return_type.php:15-17 + ; return [] RANGE[0..0] +0000 CV0($c) = RECV 1 +0001 ECHO string("message") +0002 RETURN null + +TestClass::__construct: + ; (lines=4, args=1, vars=1, tmps=1) + ; (before optimizer) + ; /.../PHP/44_verify_return_type/1_instance_44_verify_return_type/1_instance_44_verify_return_type.php:4-6 + ; return [] RANGE[0..0] +0000 CV0($val) = RECV 1 +0001 ASSIGN_OBJ THIS string("x") +0002 OP_DATA CV0($val) +0003 RETURN null + +TestClass::__toString: + ; (lines=7, args=0, vars=0, tmps=2) + ; (before optimizer) + ; /.../PHP/44_verify_return_type/1_instance_44_verify_return_type/1_instance_44_verify_return_type.php:8-11 + ; return [] RANGE[0..0] +0000 T0 = FETCH_OBJ_R THIS string("x") +0001 ECHO T0 +0002 T1 = FETCH_OBJ_R THIS string("x") +0003 VERIFY_RETURN_TYPE T1 +0004 RETURN T1 +0005 VERIFY_RETURN_TYPE +0006 RETURN null +LIVE RANGES: + 1: 0003 - 0004 (tmp/var) +``` + +
+ +
+ + +### Discovery + + +The discovery rule first gets all class names, where the method `__toString` is defined. +Afterwards it collects all `NEW` calles and filters them to see which of the instanciated objects has the method `__toString` defined. +The rule would be perfect, if we could additionally check, if one of the created objects invokes the `__toString` method within its lifetime. + +```scala +def methodClasses = cpg.method.name("__tostring").astParentFullName.l +val x44 = (name, "44_verify_return_type_i1", cpg.call("NEW").argument.filter{x => methodClasses.contains(x.code.toLowerCase)}.location.toJson); +``` + +| discovery method | expected accuracy | +|--------------------|---------------------| +| joern | FP | + +
+ +
+ + +### Measurement + + +| Tool | Comm_1 | Comm_2 | phpSAFE | Progpilot | RIPS | WAP | Ground Truth | +|-------------|----------|----------|-----------|-------------|--------|-------|----------------| +| 08 Jun 2021 | yes | no | no | yes | no | no | yes | +| 22 May 2023 | yes | no | | | | | yes | + +
+ +
+ + +### Remediation + + +Call `__toString` manually on the object, when it is passed to a function, that takes strings as arguments. + +
+ +
+ +
+ +
+ + +## 2 Instance + + +This instance shows, a function, that returns a string. When returning an object from that function, the `__toString` method of that object is invoked. + +### Code + +```PHP +x = $val; + } + + public function __toString() { + echo $this->x; // sink + return $this->x; + } +} + +// will call the function __toString(), XSS vulnerability +function F($b):string { + $class = new TestClass($b); + return $class; +} +$b = $_GET["p1"]; // source +F($b); +``` + +### Instance Properties + +| category | feature_vs_internal_api | input_sanitizer | negative_test_case | source_and_sink | +|------------|---------------------------|-------------------|----------------------|-------------------| +| S0 | FEATURE | no | no | no | + +
+ +More + +
+ + +### Compile + + +```bash +$_main: + ; (lines=8, args=0, vars=1, tmps=4) + ; (before optimizer) + ; /.../PHP/44_verify_return_type/2_instance_44_verify_return_type/2_instance_44_verify_return_type.php:1-20 + ; return [] RANGE[0..0] +0000 DECLARE_CLASS string("testclass") +0001 T1 = FETCH_R (global) string("_GET") +0002 T2 = FETCH_DIM_R T1 string("p1") +0003 ASSIGN CV0($b) T2 +0004 INIT_FCALL 1 160 string("f") +0005 SEND_VAR CV0($b) 1 +0006 DO_UCALL +0007 RETURN int(1) + +F: + ; (lines=9, args=1, vars=2, tmps=3) + ; (before optimizer) + ; /.../PHP/44_verify_return_type/2_instance_44_verify_return_type/2_instance_44_verify_return_type.php:15-18 + ; return [] RANGE[0..0] +0000 CV0($b) = RECV 1 +0001 V2 = NEW 1 string("TestClass") +0002 SEND_VAR_EX CV0($b) 1 +0003 DO_FCALL +0004 ASSIGN CV1($class) V2 +0005 VERIFY_RETURN_TYPE CV1($class) +0006 RETURN CV1($class) +0007 VERIFY_RETURN_TYPE +0008 RETURN null +LIVE RANGES: + 2: 0002 - 0004 (new) + +TestClass::__construct: + ; (lines=4, args=1, vars=1, tmps=1) + ; (before optimizer) + ; /.../PHP/44_verify_return_type/2_instance_44_verify_return_type/2_instance_44_verify_return_type.php:4-6 + ; return [] RANGE[0..0] +0000 CV0($val) = RECV 1 +0001 ASSIGN_OBJ THIS string("x") +0002 OP_DATA CV0($val) +0003 RETURN null + +TestClass::__toString: + ; (lines=7, args=0, vars=0, tmps=2) + ; (before optimizer) + ; /.../PHP/44_verify_return_type/2_instance_44_verify_return_type/2_instance_44_verify_return_type.php:8-11 + ; return [] RANGE[0..0] +0000 T0 = FETCH_OBJ_R THIS string("x") +0001 ECHO T0 +0002 T1 = FETCH_OBJ_R THIS string("x") +0003 VERIFY_RETURN_TYPE T1 +0004 RETURN T1 +0005 VERIFY_RETURN_TYPE +0006 RETURN null +LIVE RANGES: + 1: 0003 - 0004 (tmp/var) +``` + +
+ +
+ + +### Discovery + + +That rule searches for `VERIFY_RETURN_TYPE` in opcode and will find all occurences, where any function has an explicit return type. + +```scala +val x44 = (name, "44_verify_return_type_i2", cpg.call(".*VERIFY_RETURN_TYPE.*").location.toJson); +``` + +| discovery method | expected accuracy | +|--------------------|---------------------| +| joern | Perfect | + +
+ +
+ + +### Measurement + + +| Tool | Comm_1 | Comm_2 | phpSAFE | Progpilot | RIPS | WAP | Ground Truth | +|-------------|----------|----------|-----------|-------------|--------|-------|----------------| +| 08 Jun 2021 | yes | no | no | yes | no | no | yes | +| 22 May 2023 | yes | no | | | | | yes | + +
+ +
+ + +### Remediation + + +The function returning a specific type could call the conversion to that type explicitly. That might not only include calling `__toString`, but also other type conversions. + +
+ +
+ +
diff --git a/PHP/44_verify_return_type/docs/description.md b/PHP/44_verify_return_type/docs/description.md new file mode 100755 index 0000000..83145c3 --- /dev/null +++ b/PHP/44_verify_return_type/docs/description.md @@ -0,0 +1 @@ +Type declarations can be added in PHP to ensure, the arguments or the return value has the right type. If not, a `TypeError` will be thrown. If an argument or a return value does not have the right type, it will implicitly converted to that type, if possible. \ No newline at end of file diff --git a/PHP/46_object_to_array/1_instance_46_object_to_array/1_instance_46_object_to_array.bash b/PHP/46_object_to_array/1_instance_46_object_to_array/1_instance_46_object_to_array.bash old mode 100644 new mode 100755 index eeb2548..be6f069 --- a/PHP/46_object_to_array/1_instance_46_object_to_array/1_instance_46_object_to_array.bash +++ b/PHP/46_object_to_array/1_instance_46_object_to_array/1_instance_46_object_to_array.bash @@ -1,35 +1,30 @@ - -$_main: ; (lines=18, args=0, vars=3, tmps=9) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/80_object_to_array/first_ex/first_ex.php:1-16 -L0 (2): EXT_STMT -L1 (2): T3 = FETCH_R (global) string("_GET") -L2 (2): T4 = FETCH_DIM_R T3 string("p1") -L3 (2): ASSIGN CV0($b) T4 -L4 (4): NOP -L5 (5): NOP -L6 (11): EXT_STMT -L7 (11): V6 = NEW 1 string("A") -L8 (11): SEND_VAR_EX CV0($b) 1 -L9 (11): DO_FCALL -L10 (11): ASSIGN CV1($a) V6 -L11 (13): EXT_STMT -L12 (13): T9 = CAST (array) CV1($a) -L13 (13): ASSIGN CV2($arr) T9 -L14 (16): EXT_STMT -L15 (16): T11 = FETCH_DIM_R CV2($arr) string("b") -L16 (16): ECHO T11 -L17 (16): RETURN int(1) -LIVE RANGES: - 6: L8 - L10 (new) - -A::__construct: ; (lines=7, args=1, vars=1, tmps=1) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/80_object_to_array/first_ex/first_ex.php:7-9 -L0 (7): EXT_NOP -L1 (7): CV0($b) = RECV 1 -L2 (8): EXT_STMT -L3 (8): ASSIGN_OBJ THIS string("b") -L4 (8): OP_DATA CV0($b) -L5 (9): EXT_STMT -L6 (9): RETURN null + +$_main: + ; (lines=12, args=0, vars=3, tmps=9) + ; (before optimizer) + ; /.../PHP/46_object_to_array/1_instance_46_object_to_array/1_instance_46_object_to_array.php:1-16 + ; return [] RANGE[0..0] +0000 T3 = FETCH_R (global) string("_GET") +0001 T4 = FETCH_DIM_R T3 string("p1") +0002 ASSIGN CV0($b) T4 +0003 V6 = NEW 1 string("A") +0004 SEND_VAR_EX CV0($b) 1 +0005 DO_FCALL +0006 ASSIGN CV1($a) V6 +0007 T9 = CAST (array) CV1($a) +0008 ASSIGN CV2($arr) T9 +0009 T11 = FETCH_DIM_R CV2($arr) string("y") +0010 ECHO T11 +0011 RETURN int(1) +LIVE RANGES: + 6: 0004 - 0006 (new) + +A::__construct: + ; (lines=4, args=1, vars=1, tmps=1) + ; (before optimizer) + ; /.../PHP/46_object_to_array/1_instance_46_object_to_array/1_instance_46_object_to_array.php:6-8 + ; return [] RANGE[0..0] +0000 CV0($b) = RECV 1 +0001 ASSIGN_OBJ THIS string("y") +0002 OP_DATA CV0($b) +0003 RETURN null diff --git a/PHP/46_object_to_array/1_instance_46_object_to_array/1_instance_46_object_to_array.json b/PHP/46_object_to_array/1_instance_46_object_to_array/1_instance_46_object_to_array.json old mode 100644 new mode 100755 index 6267afd..adc3eb9 --- a/PHP/46_object_to_array/1_instance_46_object_to_array/1_instance_46_object_to_array.json +++ b/PHP/46_object_to_array/1_instance_46_object_to_array/1_instance_46_object_to_array.json @@ -1,37 +1,38 @@ -{ - "code": { - "path": "./1_instance_46_object_to_array.php", - "injection_skeleton_broken": false - }, - "discovery": { - "rule": "./1_instance_46_object_to_array.sc", - "method": "joern", - "rule_accuracy": null, - "notes": null - }, - "compile": { - "binary": "./1_instance_46_object_to_array.bash", - "instruction": null, - "dependencies": null - }, - "expectation": { - "type": "xss", - "sink_file": "./1_instance_46_object_to_array.php", - "sink_line": 16, - "source_file": "./1_instance_46_object_to_array.php", - "source_line": 2, - "expectation": true - }, - "properties": { - "category": "D2", - "feature_vs_internal_api": "FEATURE", - "input_sanitizer": false, - "source_and_sink": false, - "negative_test_case": false - }, - "remediation": { - "notes": "", - "transformation": null, - "modeling_rule": null - } +{ + "description": "This instance creates an object and casts that object to an array.", + "code": { + "path": "./1_instance_46_object_to_array.php", + "injection_skeleton_broken": false + }, + "discovery": { + "rule": "./1_instance_46_object_to_array.sc", + "method": "joern", + "rule_accuracy": "Perfect", + "notes": "The rule looks for all casts and checks for which of them the argument is an array." + }, + "compile": { + "binary": "./1_instance_46_object_to_array.bash", + "instruction": null, + "dependencies": null + }, + "expectation": { + "type": "xss", + "sink_file": "./1_instance_46_object_to_array.php", + "sink_line": 16, + "source_file": "./1_instance_46_object_to_array.php", + "source_line": 10, + "expectation": true + }, + "properties": { + "category": "D2", + "feature_vs_internal_api": "FEATURE", + "input_sanitizer": false, + "source_and_sink": false, + "negative_test_case": false + }, + "remediation": { + "notes": "", + "transformation": null, + "modeling_rule": null + } } \ No newline at end of file diff --git a/PHP/46_object_to_array/1_instance_46_object_to_array/1_instance_46_object_to_array.php b/PHP/46_object_to_array/1_instance_46_object_to_array/1_instance_46_object_to_array.php old mode 100644 new mode 100755 index f24c497..9159d0d --- a/PHP/46_object_to_array/1_instance_46_object_to_array/1_instance_46_object_to_array.php +++ b/PHP/46_object_to_array/1_instance_46_object_to_array/1_instance_46_object_to_array.php @@ -1,16 +1,16 @@ -b = $b; - } -} -$a = new A($b); -// object to array -$arr = (array) $a; -// print the property $b in the object -// XSS vulnerability -echo $arr['b']; \ No newline at end of file +y = $b; + } +} +$b = $_GET["p1"]; // source +$a = new A($b); +// object to array +$arr = (array) $a; +// print the property $b in the object +// XSS vulnerability +echo $arr['y']; // sink \ No newline at end of file diff --git a/PHP/46_object_to_array/1_instance_46_object_to_array/1_instance_46_object_to_array.sc b/PHP/46_object_to_array/1_instance_46_object_to_array/1_instance_46_object_to_array.sc old mode 100644 new mode 100755 index a728da3..fb4effa --- a/PHP/46_object_to_array/1_instance_46_object_to_array/1_instance_46_object_to_array.sc +++ b/PHP/46_object_to_array/1_instance_46_object_to_array/1_instance_46_object_to_array.sc @@ -1,6 +1,6 @@ -@main def main(name : String): Unit = { - importCpg(name) - val x46 = (name, "46_object_to_array_iall", cpg.call(".*CAST.*").argument.order(0).code("array").astParent.location.toJson); - println(x46) - delete; +@main def main(name : String): Unit = { + importCpg(name) + val x46 = (name, "46_object_to_array_iall", cpg.call(".*CAST.*").argument.order(0).code("array").astParent.location.toJson); + println(x46) + delete; } \ No newline at end of file diff --git a/PHP/46_object_to_array/46_object_to_array.json b/PHP/46_object_to_array/46_object_to_array.json old mode 100644 new mode 100755 index 2d30c6b..6245dec --- a/PHP/46_object_to_array/46_object_to_array.json +++ b/PHP/46_object_to_array/46_object_to_array.json @@ -1,14 +1,14 @@ -{ - "name": "Object to Array", - "description": "", - "family": "code_pattern_php", - "tags": [ - "sast", - "php", - "php_v7.4.9" - ], - "instances": [ - "./1_instance_46_object_to_array/1_instance_46_object_to_array.json" - ], - "version": "v0.draft" +{ + "name": "Object To Array", + "description": "./docs/description.md", + "family": "code_pattern_php", + "tags": [ + "sast", + "php", + "php_v7.4.9" + ], + "instances": [ + "./1_instance_46_object_to_array/1_instance_46_object_to_array.json" + ], + "version": "v1.0" } \ No newline at end of file diff --git a/PHP/46_object_to_array/Pattern Convert object to array.md b/PHP/46_object_to_array/Pattern Convert object to array.md deleted file mode 100644 index 6e932b5..0000000 --- a/PHP/46_object_to_array/Pattern Convert object to array.md +++ /dev/null @@ -1,99 +0,0 @@ -# Pattern: Convert Object to Array - -## Category - -Objects - -## Definition - -## Instances - -### Instance 1 - -- CATEGORY: D2 -- FEATURE vs INTERNAL API: FEATURE -- INPUT SANITIZERS: NO -- SOURCES AND SINKS: NO -- NEGATIVE TEST CASES: NO -- CODE: - -```php -b = $b; - } -} -$a = new A($b); -// object to array -$arr = (array) $a; -// print the property $b in the object -// XSS vulnerability -echo $arr['b']; -``` - -- MEASUREMENT: - -| Tool | RIPS | phpSAFE | WAP | Progpilot | Comm_1 | Comm_2 | Correct | -| ------------- | ---- | ------- | ---- | --------- | ------- | --------- | ------- | -| Vulnerability | NO | NO | NO | NO | NO | NO | YES | -Measurements Date: 8 June 2021 - -- OPCODE: - -```bash -$_main: ; (lines=18, args=0, vars=3, tmps=9) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/80_object_to_array/first_ex/first_ex.php:1-16 -L0 (2): EXT_STMT -L1 (2): T3 = FETCH_R (global) string("_GET") -L2 (2): T4 = FETCH_DIM_R T3 string("p1") -L3 (2): ASSIGN CV0($b) T4 -L4 (4): NOP -L5 (5): NOP -L6 (11): EXT_STMT -L7 (11): V6 = NEW 1 string("A") -L8 (11): SEND_VAR_EX CV0($b) 1 -L9 (11): DO_FCALL -L10 (11): ASSIGN CV1($a) V6 -L11 (13): EXT_STMT -L12 (13): T9 = CAST (array) CV1($a) -L13 (13): ASSIGN CV2($arr) T9 -L14 (16): EXT_STMT -L15 (16): T11 = FETCH_DIM_R CV2($arr) string("b") -L16 (16): ECHO T11 -L17 (16): RETURN int(1) -LIVE RANGES: - 6: L8 - L10 (new) - -A::__construct: ; (lines=7, args=1, vars=1, tmps=1) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/80_object_to_array/first_ex/first_ex.php:7-9 -L0 (7): EXT_NOP -L1 (7): CV0($b) = RECV 1 -L2 (8): EXT_STMT -L3 (8): ASSIGN_OBJ THIS string("b") -L4 (8): OP_DATA CV0($b) -L5 (9): EXT_STMT -L6 (9): RETURN null -``` - -- DISCOVERY: - -```bash -cpg.call(".*CAST.*").argument.order(0).code("array").location.l -``` - -- PRECONDITIONS: - 1. - -- TRANSFORMATION: - -``` - -``` - diff --git a/PHP/46_object_to_array/README.md b/PHP/46_object_to_array/README.md new file mode 100755 index 0000000..c7257da --- /dev/null +++ b/PHP/46_object_to_array/README.md @@ -0,0 +1,125 @@ +[//]: # (This file is automatically generated. If you wish to make any changes, please use the JSON files and regenerate this file using the tpframework.) + +# Object To Array + +Tags: sast, php, php_v7.4.9 + +Version: v1.0 + +## Description + +In PHP you can cast any object to a simple 1D array using `(array)`. + +## Overview + +| Instances | has discovery rule | discovery method | rule successfull | +|---------------------------|----------------------|--------------------|--------------------| +| [1 Instance](#1-instance) | yes | joern | yes | + +## 1 Instance + +This instance creates an object and casts that object to an array. + +### Code + +```PHP +y = $b; + } +} +$b = $_GET["p1"]; // source +$a = new A($b); +// object to array +$arr = (array) $a; +// print the property $b in the object +// XSS vulnerability +echo $arr['y']; // sink +``` + +### Instance Properties + +| category | feature_vs_internal_api | input_sanitizer | negative_test_case | source_and_sink | +|------------|---------------------------|-------------------|----------------------|-------------------| +| D2 | FEATURE | no | no | no | + +
+ +More + +
+ + +### Compile + + +```bash +$_main: + ; (lines=12, args=0, vars=3, tmps=9) + ; (before optimizer) + ; /.../PHP/46_object_to_array/1_instance_46_object_to_array/1_instance_46_object_to_array.php:1-16 + ; return [] RANGE[0..0] +0000 T3 = FETCH_R (global) string("_GET") +0001 T4 = FETCH_DIM_R T3 string("p1") +0002 ASSIGN CV0($b) T4 +0003 V6 = NEW 1 string("A") +0004 SEND_VAR_EX CV0($b) 1 +0005 DO_FCALL +0006 ASSIGN CV1($a) V6 +0007 T9 = CAST (array) CV1($a) +0008 ASSIGN CV2($arr) T9 +0009 T11 = FETCH_DIM_R CV2($arr) string("y") +0010 ECHO T11 +0011 RETURN int(1) +LIVE RANGES: + 6: 0004 - 0006 (new) + +A::__construct: + ; (lines=4, args=1, vars=1, tmps=1) + ; (before optimizer) + ; /.../PHP/46_object_to_array/1_instance_46_object_to_array/1_instance_46_object_to_array.php:6-8 + ; return [] RANGE[0..0] +0000 CV0($b) = RECV 1 +0001 ASSIGN_OBJ THIS string("y") +0002 OP_DATA CV0($b) +0003 RETURN null +``` + +
+ +
+ + +### Discovery + + +The rule looks for all casts and checks for which of them the argument is an array. + +```scala +val x46 = (name, "46_object_to_array_iall", cpg.call(".*CAST.*").argument.order(0).code("array").astParent.location.toJson); +``` + +| discovery method | expected accuracy | +|--------------------|---------------------| +| joern | Perfect | + +
+ +
+ + +### Measurement + + +| Tool | Comm_1 | Comm_2 | phpSAFE | Progpilot | RIPS | WAP | Ground Truth | +|-------------|----------|----------|-----------|-------------|--------|-------|----------------| +| 08 Jun 2021 | no | no | no | no | no | no | yes | +| 22 May 2023 | no | yes | | | | | yes | + +
+ +
diff --git a/PHP/46_object_to_array/docs/description.md b/PHP/46_object_to_array/docs/description.md new file mode 100755 index 0000000..6305db3 --- /dev/null +++ b/PHP/46_object_to_array/docs/description.md @@ -0,0 +1 @@ +In PHP you can cast any object to a simple 1D array using `(array)`. \ No newline at end of file diff --git a/PHP/48_construct_with_inheritance/1_instance_48_construct_with_inheritance/1_instance_48_construct_with_inheritance.bash b/PHP/48_construct_with_inheritance/1_instance_48_construct_with_inheritance/1_instance_48_construct_with_inheritance.bash old mode 100644 new mode 100755 index 132aa3a..c008d35 --- a/PHP/48_construct_with_inheritance/1_instance_48_construct_with_inheritance/1_instance_48_construct_with_inheritance.bash +++ b/PHP/48_construct_with_inheritance/1_instance_48_construct_with_inheritance/1_instance_48_construct_with_inheritance.bash @@ -1,35 +1,26 @@ - -$_main: ; (lines=9, args=0, vars=1, tmps=5) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/27_construct_with_inheritance/first_ex/first_ex.php:1-18 -L0 (18): EXT_STMT -L1 (18): V1 = NEW 1 string("child_class") -L2 (18): CHECK_FUNC_ARG 1 -L3 (18): V2 = FETCH_FUNC_ARG (global) string("_GET") -L4 (18): V3 = FETCH_DIM_FUNC_ARG V2 string("p1") -L5 (18): SEND_FUNC_ARG V3 1 -L6 (18): DO_FCALL -L7 (18): ASSIGN CV0($b) V1 -L8 (18): RETURN int(1) -LIVE RANGES: - 1: L2 - L7 (new) - -parent_class::__construct: ; (lines=6, args=1, vars=1, tmps=0) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/27_construct_with_inheritance/first_ex/first_ex.php:5-7 -L0 (5): EXT_NOP -L1 (5): CV0($b) = RECV 1 -L2 (6): EXT_STMT -L3 (6): ECHO CV0($b) -L4 (7): EXT_STMT -L5 (7): RETURN null - -parent_class::F: ; (lines=6, args=1, vars=2, tmps=1) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/27_construct_with_inheritance/first_ex/first_ex.php:9-11 -L0 (9): EXT_NOP -L1 (9): CV0($b) = RECV 1 -L2 (10): EXT_STMT -L3 (10): ASSIGN CV1($a) CV0($b) -L4 (11): EXT_STMT -L5 (11): RETURN null + +$_main: + ; (lines=9, args=0, vars=2, tmps=6) + ; (before optimizer) + ; /.../PHP/48_construct_with_inheritance/1_instance_48_construct_with_inheritance/1_instance_48_construct_with_inheritance.php:1-16 + ; return [] RANGE[0..0] +0000 T2 = FETCH_R (global) string("_GET") +0001 T3 = FETCH_DIM_R T2 string("p1") +0002 ASSIGN CV0($b) T3 +0003 V5 = NEW 1 string("child_class") +0004 SEND_VAR_EX CV0($b) 1 +0005 DO_FCALL +0006 ASSIGN CV1($a) V5 +0007 ECHO CV1($a) +0008 RETURN int(1) +LIVE RANGES: + 5: 0004 - 0006 (new) + +parent_class::__construct: + ; (lines=3, args=1, vars=1, tmps=0) + ; (before optimizer) + ; /.../PHP/48_construct_with_inheritance/1_instance_48_construct_with_inheritance/1_instance_48_construct_with_inheritance.php:4-6 + ; return [] RANGE[0..0] +0000 CV0($b) = RECV 1 +0001 RETURN CV0($b) +0002 RETURN null diff --git a/PHP/48_construct_with_inheritance/1_instance_48_construct_with_inheritance/1_instance_48_construct_with_inheritance.json b/PHP/48_construct_with_inheritance/1_instance_48_construct_with_inheritance/1_instance_48_construct_with_inheritance.json old mode 100644 new mode 100755 index bff9e1d..417e658 --- a/PHP/48_construct_with_inheritance/1_instance_48_construct_with_inheritance/1_instance_48_construct_with_inheritance.json +++ b/PHP/48_construct_with_inheritance/1_instance_48_construct_with_inheritance/1_instance_48_construct_with_inheritance.json @@ -1,37 +1,38 @@ -{ - "code": { - "path": "./1_instance_48_construct_with_inheritance.php", - "injection_skeleton_broken": false - }, - "discovery": { - "rule": "./1_instance_48_construct_with_inheritance.sc", - "method": "joern", - "rule_accuracy": null, - "notes": null - }, - "compile": { - "binary": "./1_instance_48_construct_with_inheritance.bash", - "instruction": null, - "dependencies": null - }, - "expectation": { - "type": "xss", - "sink_file": "./1_instance_48_construct_with_inheritance.php", - "sink_line": 6, - "source_file": "./1_instance_48_construct_with_inheritance.php", - "source_line": 18, - "expectation": true - }, - "properties": { - "category": "S0", - "feature_vs_internal_api": "FEATURE", - "input_sanitizer": false, - "source_and_sink": false, - "negative_test_case": false - }, - "remediation": { - "notes": "", - "transformation": null, - "modeling_rule": null - } +{ + "description": "This instance shows a construct with inheritance, where the child class is instantiated and the constructor from the parent class is called.", + "code": { + "path": "./1_instance_48_construct_with_inheritance.php", + "injection_skeleton_broken": false + }, + "discovery": { + "rule": "./1_instance_48_construct_with_inheritance.sc", + "method": "joern", + "rule_accuracy": null, + "notes": "This pattern would profit from source code discovery. As inheritance cannot be detected on opcode level." + }, + "compile": { + "binary": "./1_instance_48_construct_with_inheritance.bash", + "instruction": null, + "dependencies": null + }, + "expectation": { + "type": "xss", + "sink_file": "./1_instance_48_construct_with_inheritance.php", + "sink_line": 15, + "source_file": "./1_instance_48_construct_with_inheritance.php", + "source_line": 13, + "expectation": true + }, + "properties": { + "category": "S0", + "feature_vs_internal_api": "FEATURE", + "input_sanitizer": false, + "source_and_sink": false, + "negative_test_case": false + }, + "remediation": { + "notes": "", + "transformation": null, + "modeling_rule": null + } } \ No newline at end of file diff --git a/PHP/48_construct_with_inheritance/1_instance_48_construct_with_inheritance/1_instance_48_construct_with_inheritance.php b/PHP/48_construct_with_inheritance/1_instance_48_construct_with_inheritance/1_instance_48_construct_with_inheritance.php old mode 100644 new mode 100755 index d389f52..060fafd --- a/PHP/48_construct_with_inheritance/1_instance_48_construct_with_inheritance/1_instance_48_construct_with_inheritance.php +++ b/PHP/48_construct_with_inheritance/1_instance_48_construct_with_inheritance/1_instance_48_construct_with_inheritance.php @@ -1,18 +1,15 @@ - (x.name,x.astParentFullName)}.l; - println(x) - println("*********************************************") - delete; +@main def main(name : String): Unit = { + importCpg(name) + // DOES NOT WORK + val x48 = cpg.method.astParentType("TYPE_DECL").map{x => (x.name,x.astParentFullName)}.l; + println(x48) + delete; } \ No newline at end of file diff --git a/PHP/48_construct_with_inheritance/48_construct_with_inheritance.json b/PHP/48_construct_with_inheritance/48_construct_with_inheritance.json old mode 100644 new mode 100755 index 2a0a22a..ff31242 --- a/PHP/48_construct_with_inheritance/48_construct_with_inheritance.json +++ b/PHP/48_construct_with_inheritance/48_construct_with_inheritance.json @@ -1,14 +1,14 @@ -{ - "name": "Construct With Inheritance", - "description": "", - "family": "code_pattern_php", - "tags": [ - "sast", - "php", - "php_v7.4.9" - ], - "instances": [ - "./1_instance_48_construct_with_inheritance/1_instance_48_construct_with_inheritance.json" - ], - "version": "v0.draft" +{ + "name": "Construct With Inheritance", + "description": "./docs/description.md", + "family": "code_pattern_php", + "tags": [ + "sast", + "php", + "php_v7.4.9" + ], + "instances": [ + "./1_instance_48_construct_with_inheritance/1_instance_48_construct_with_inheritance.json" + ], + "version": "v1.0" } \ No newline at end of file diff --git a/PHP/48_construct_with_inheritance/Pattern Construct With Inheretence.md b/PHP/48_construct_with_inheritance/Pattern Construct With Inheretence.md deleted file mode 100644 index fcbc90a..0000000 --- a/PHP/48_construct_with_inheritance/Pattern Construct With Inheretence.md +++ /dev/null @@ -1,118 +0,0 @@ -# Pattern: Construct with Inheritance - -## Category - -Objects - -## Definition - -## Instances - -### Instance 1 - -- CATEGORY: S0 -- FEATURE vs INTERNAL API: FEATURE -- INPUT SANITIZERS: NO -- SOURCES AND SINKS: NO -- NEGATIVE TEST CASES: NO -- CODE: - -```php - + +More + +
+ + +### Compile + + +```bash +$_main: + ; (lines=9, args=0, vars=2, tmps=6) + ; (before optimizer) + ; /.../PHP/48_construct_with_inheritance/1_instance_48_construct_with_inheritance/1_instance_48_construct_with_inheritance.php:1-16 + ; return [] RANGE[0..0] +0000 T2 = FETCH_R (global) string("_GET") +0001 T3 = FETCH_DIM_R T2 string("p1") +0002 ASSIGN CV0($b) T3 +0003 V5 = NEW 1 string("child_class") +0004 SEND_VAR_EX CV0($b) 1 +0005 DO_FCALL +0006 ASSIGN CV1($a) V5 +0007 ECHO CV1($a) +0008 RETURN int(1) +LIVE RANGES: + 5: 0004 - 0006 (new) + +parent_class::__construct: + ; (lines=3, args=1, vars=1, tmps=0) + ; (before optimizer) + ; /.../PHP/48_construct_with_inheritance/1_instance_48_construct_with_inheritance/1_instance_48_construct_with_inheritance.php:4-6 + ; return [] RANGE[0..0] +0000 CV0($b) = RECV 1 +0001 RETURN CV0($b) +0002 RETURN null +``` + +
+ +
+ + +### Discovery + + +This pattern would profit from source code discovery. As inheritance cannot be detected on opcode level. + +```scala +// DOES NOT WORK +val x48 = cpg.method.astParentType("TYPE_DECL").map{x => (x.name,x.astParentFullName)}.l; +``` + +| discovery method | expected accuracy | +|--------------------|---------------------| +| joern | | + +
+ +
+ + +### Measurement + + +| Tool | Comm_1 | Comm_2 | phpSAFE | Progpilot | RIPS | WAP | Ground Truth | +|-------------|----------|----------|-----------|-------------|--------|-------|----------------| +| 08 Jun 2021 | yes | no | no | yes | no | no | yes | +| 22 May 2023 | no | yes | | | | | yes | + +
+ + diff --git a/PHP/48_construct_with_inheritance/docs/description.md b/PHP/48_construct_with_inheritance/docs/description.md new file mode 100755 index 0000000..2720822 --- /dev/null +++ b/PHP/48_construct_with_inheritance/docs/description.md @@ -0,0 +1 @@ +When a child class inherits from a parent class and the child class does not implement a certain method, the method defined in parent class will be used. \ No newline at end of file diff --git a/PHP/49_static_instance/1_instance_49_static_instance/1_instance_49_static_instance.bash b/PHP/49_static_instance/1_instance_49_static_instance/1_instance_49_static_instance.bash old mode 100644 new mode 100755 index b872f34..903809a --- a/PHP/49_static_instance/1_instance_49_static_instance/1_instance_49_static_instance.bash +++ b/PHP/49_static_instance/1_instance_49_static_instance/1_instance_49_static_instance.bash @@ -1,44 +1,39 @@ - -$_main: ; (lines=16, args=0, vars=0, tmps=6) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/126_static_instance/126_static_instance.php:1-17 -L0 (4): NOP -L1 (5): NOP -L2 (16): EXT_STMT -L3 (16): INIT_STATIC_METHOD_CALL 0 string("Myclass") string("oneInstance") -L4 (16): V0 = DO_FCALL -L5 (16): V0 = SEPARATE V0 -L6 (16): T2 = FETCH_R (global) string("_GET") -L7 (16): T3 = FETCH_DIM_R T2 string("p1") -L8 (16): ASSIGN_OBJ V0 string("x") -L9 (16): OP_DATA T3 -L10 (17): EXT_STMT -L11 (17): INIT_STATIC_METHOD_CALL 0 string("Myclass") string("oneInstance") -L12 (17): V4 = DO_FCALL -L13 (17): T5 = FETCH_OBJ_R V4 string("x") -L14 (17): ECHO T5 -L15 (17): RETURN int(1) -LIVE RANGES: - 0: L6 - L8 (tmp/var) - -Myclass::oneInstance: ; (lines=16, args=0, vars=0, tmps=7) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/126_static_instance/126_static_instance.php:7-13 -L0 (7): EXT_NOP -L1 (9): EXT_STMT -L2 (9): T0 = FETCH_STATIC_PROP_R string("instance") (self) (exception) -L3 (9): T1 = INSTANCEOF T0 (self) (no-autolod) (exception) -L4 (9): T2 = BOOL_NOT T1 -L5 (9): JMPZ T2 L11 -L6 (10): EXT_STMT -L7 (10): V4 = NEW 0 (self) (exception) -L8 (10): DO_FCALL -L9 (10): ASSIGN_STATIC_PROP string("instance") -L10 (10): OP_DATA V4 -L11 (12): EXT_STMT -L12 (12): T6 = FETCH_STATIC_PROP_R string("instance") (self) (exception) -L13 (12): RETURN T6 -L14 (13): EXT_STMT -L15 (13): RETURN null -LIVE RANGES: - 4: L8 - L9 (new) + +$_main: + ; (lines=14, args=0, vars=2, tmps=8) + ; (before optimizer) + ; /.../PHP/49_static_instance/1_instance_49_static_instance/1_instance_49_static_instance.php:1-17 + ; return [] RANGE[0..0] +0000 T2 = FETCH_R (global) string("_GET") +0001 T3 = FETCH_DIM_R T2 string("p1") +0002 ASSIGN CV0($b) T3 +0003 INIT_STATIC_METHOD_CALL 0 string("Myclass") string("oneInstance") +0004 V5 = DO_UCALL +0005 V5 = SEPARATE V5 +0006 ASSIGN_OBJ V5 string("x") +0007 OP_DATA CV0($b) +0008 INIT_STATIC_METHOD_CALL 0 string("Myclass") string("oneInstance") +0009 V7 = DO_UCALL +0010 T8 = FETCH_OBJ_R V7 string("x") +0011 ASSIGN CV1($a) T8 +0012 ECHO CV1($a) +0013 RETURN int(1) + +Myclass::oneInstance: + ; (lines=11, args=0, vars=0, tmps=7) + ; (before optimizer) + ; /.../PHP/49_static_instance/1_instance_49_static_instance/1_instance_49_static_instance.php:6-11 + ; return [] RANGE[0..0] +0000 T0 = FETCH_STATIC_PROP_R string("instance") (self) (exception) +0001 T1 = INSTANCEOF T0 (self) (no-autoload) (silent) (exception) +0002 T2 = BOOL_NOT T1 +0003 JMPZ T2 0008 +0004 V4 = NEW 0 (self) (exception) +0005 DO_FCALL +0006 ASSIGN_STATIC_PROP string("instance") +0007 OP_DATA V4 +0008 T6 = FETCH_STATIC_PROP_R string("instance") (self) (exception) +0009 RETURN T6 +0010 RETURN null +LIVE RANGES: + 4: 0005 - 0006 (new) diff --git a/PHP/49_static_instance/1_instance_49_static_instance/1_instance_49_static_instance.json b/PHP/49_static_instance/1_instance_49_static_instance/1_instance_49_static_instance.json old mode 100644 new mode 100755 index d9c5ec8..0f39ec5 --- a/PHP/49_static_instance/1_instance_49_static_instance/1_instance_49_static_instance.json +++ b/PHP/49_static_instance/1_instance_49_static_instance/1_instance_49_static_instance.json @@ -1,37 +1,38 @@ -{ - "code": { - "path": "./1_instance_49_static_instance.php", - "injection_skeleton_broken": false - }, - "discovery": { - "rule": "./1_instance_49_static_instance.sc", - "method": "joern", - "rule_accuracy": null, - "notes": null - }, - "compile": { - "binary": "./1_instance_49_static_instance.bash", - "instruction": null, - "dependencies": null - }, - "expectation": { - "type": "xss", - "sink_file": "./1_instance_49_static_instance.php", - "sink_line": 17, - "source_file": "./1_instance_49_static_instance.php", - "source_line": 16, - "expectation": true - }, - "properties": { - "category": "S0", - "feature_vs_internal_api": "FEATURE", - "input_sanitizer": false, - "source_and_sink": false, - "negative_test_case": false - }, - "remediation": { - "notes": "", - "transformation": null, - "modeling_rule": null - } +{ + "description": "This instance shows a class, that has only one static instance.", + "code": { + "path": "./1_instance_49_static_instance.php", + "injection_skeleton_broken": false + }, + "discovery": { + "rule": "./1_instance_49_static_instance.sc", + "method": "joern", + "rule_accuracy": "FP", + "notes": null + }, + "compile": { + "binary": "./1_instance_49_static_instance.bash", + "instruction": null, + "dependencies": null + }, + "expectation": { + "type": "xss", + "sink_file": "./1_instance_49_static_instance.php", + "sink_line": 17, + "source_file": "./1_instance_49_static_instance.php", + "source_line": 14, + "expectation": true + }, + "properties": { + "category": "S0", + "feature_vs_internal_api": "FEATURE", + "input_sanitizer": false, + "source_and_sink": false, + "negative_test_case": false + }, + "remediation": { + "notes": "", + "transformation": null, + "modeling_rule": null + } } \ No newline at end of file diff --git a/PHP/49_static_instance/1_instance_49_static_instance/1_instance_49_static_instance.php b/PHP/49_static_instance/1_instance_49_static_instance/1_instance_49_static_instance.php old mode 100644 new mode 100755 index c82edfd..43a574b --- a/PHP/49_static_instance/1_instance_49_static_instance/1_instance_49_static_instance.php +++ b/PHP/49_static_instance/1_instance_49_static_instance/1_instance_49_static_instance.php @@ -1,17 +1,17 @@ -x = $_GET["p1"]; -echo Myclass::oneInstance()->x; \ No newline at end of file +x = $b; +$a = Myclass::oneInstance()->x; +echo $a; // sink \ No newline at end of file diff --git a/PHP/49_static_instance/1_instance_49_static_instance/1_instance_49_static_instance.sc b/PHP/49_static_instance/1_instance_49_static_instance/1_instance_49_static_instance.sc old mode 100644 new mode 100755 index 0875791..37e1085 --- a/PHP/49_static_instance/1_instance_49_static_instance/1_instance_49_static_instance.sc +++ b/PHP/49_static_instance/1_instance_49_static_instance/1_instance_49_static_instance.sc @@ -1,17 +1,14 @@ -@main def main(name : String): Unit = { - importCpg(name) - val x47 = (name, "49_static_instance", cpg.method.filter{ m => - -val fieldsHoldingConstruct = m.call.name(".*__construct.*").cfgNext.isLiteral.where(_.inCall.name("ASSIGN_STATIC_PROP_1")).code.l - -val returnedVars = m.call.name("FETCH_STATIC_PROP_R").inCall.name("=").filter{ assign => - val target = assign.argument.order(0).code.headOption - target.isDefined && assign.cfgNext.cfgNext.isCall.codeExact(s"RETURN ${target.get}").size > 0 - }.flatMap{ assign => assign.argument.order(1).isCall.argument.order(0).isLiteral.code.headOption }.l - - (returnedVars intersect fieldsHoldingConstruct).size > 0 - - }.location.toJson); - println(x47) - delete; -} \ No newline at end of file +@main def main(name : String): Unit = { + importCpg(name) + val x49 = (name, "49_static_instance", cpg.method.filter{ m => + val fieldsHoldingConstruct = m.call.name(".*__construct.*").cfgNext.isLiteral.where(_.inCall.name("ASSIGN_STATIC_PROP_1")).code.l + val returnedVars = m.call.name("FETCH_STATIC_PROP_R").inCall.name("=").filter{ assign => + val target = assign.argument.order(0).code.headOption + target.isDefined && assign.cfgNext.cfgNext.isCall.codeExact(s"RETURN ${target.get}").size > 0 + }.flatMap{ assign => assign.argument.order(1).isCall.argument.order(0).isLiteral.code.headOption }.l + (returnedVars intersect fieldsHoldingConstruct).size > 0 + + }.location.toJson); + println(x49) + delete; +} \ No newline at end of file diff --git a/PHP/49_static_instance/49_static_instance.json b/PHP/49_static_instance/49_static_instance.json old mode 100644 new mode 100755 index 5b0a5da..4d05610 --- a/PHP/49_static_instance/49_static_instance.json +++ b/PHP/49_static_instance/49_static_instance.json @@ -1,14 +1,14 @@ -{ - "name": "Static Instance", - "description": "", - "family": "code_pattern_php", - "tags": [ - "sast", - "php", - "php_v7.4.9" - ], - "instances": [ - "./1_instance_49_static_instance/1_instance_49_static_instance.json" - ], - "version": "v0.draft" +{ + "name": "Static Instance", + "description": "./docs/description.md", + "family": "code_pattern_php", + "tags": [ + "sast", + "php", + "php_v7.4.9" + ], + "instances": [ + "./1_instance_49_static_instance/1_instance_49_static_instance.json" + ], + "version": "v1.0" } \ No newline at end of file diff --git a/PHP/49_static_instance/Pattern Static Instance.md b/PHP/49_static_instance/Pattern Static Instance.md deleted file mode 100644 index 082adae..0000000 --- a/PHP/49_static_instance/Pattern Static Instance.md +++ /dev/null @@ -1,120 +0,0 @@ -# Pattern: Static Instance - -## Category - -Objects - -## Definition - -## Instances - -### Instance 1 - -- CATEGORY: S0 -- FEATURE vs INTERNAL API: FEATURE -- INPUT SANITIZERS: NO -- SOURCES AND SINKS: NO -- NEGATIVE TEST CASES: NO -- CODE: - -```php -x = $_GET["p1"]; -echo Myclass::oneInstance()->x; -``` - -- MEASUREMENT: - -| Tool | RIPS | phpSAFE | WAP | Progpilot | Comm_1 | Comm_2 | Correct | -| ------------- | ---- | ------- | ---- | --------- | ------- | --------- | ------- | -| Vulnerability | NO | NO | NO | NO | NO | NO | YES | -Measurements Date: 8 June 2021 - -- OPCODE: - -```bash -$_main: ; (lines=16, args=0, vars=0, tmps=6) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/126_static_instance/126_static_instance.php:1-17 -L0 (4): NOP -L1 (5): NOP -L2 (16): EXT_STMT -L3 (16): INIT_STATIC_METHOD_CALL 0 string("Myclass") string("oneInstance") -L4 (16): V0 = DO_FCALL -L5 (16): V0 = SEPARATE V0 -L6 (16): T2 = FETCH_R (global) string("_GET") -L7 (16): T3 = FETCH_DIM_R T2 string("p1") -L8 (16): ASSIGN_OBJ V0 string("x") -L9 (16): OP_DATA T3 -L10 (17): EXT_STMT -L11 (17): INIT_STATIC_METHOD_CALL 0 string("Myclass") string("oneInstance") -L12 (17): V4 = DO_FCALL -L13 (17): T5 = FETCH_OBJ_R V4 string("x") -L14 (17): ECHO T5 -L15 (17): RETURN int(1) -LIVE RANGES: - 0: L6 - L8 (tmp/var) - -Myclass::oneInstance: ; (lines=16, args=0, vars=0, tmps=7) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/126_static_instance/126_static_instance.php:7-13 -L0 (7): EXT_NOP -L1 (9): EXT_STMT -L2 (9): T0 = FETCH_STATIC_PROP_R string("instance") (self) (exception) -L3 (9): T1 = INSTANCEOF T0 (self) (no-autolod) (exception) -L4 (9): T2 = BOOL_NOT T1 -L5 (9): JMPZ T2 L11 -L6 (10): EXT_STMT -L7 (10): V4 = NEW 0 (self) (exception) -L8 (10): DO_FCALL -L9 (10): ASSIGN_STATIC_PROP string("instance") -L10 (10): OP_DATA V4 -L11 (12): EXT_STMT -L12 (12): T6 = FETCH_STATIC_PROP_R string("instance") (self) (exception) -L13 (12): RETURN T6 -L14 (13): EXT_STMT -L15 (13): RETURN null -LIVE RANGES: - 4: L8 - L9 (new) -``` - -- DISCOVERY: - -```bash -cpg.method.filter{ m => - -val fieldsHoldingConstruct = m.call.name(".*__construct.*").cfgNext.isLiteral.where(_.inCall.name("ASSIGN_STATIC_PROP_1")).code.l - -val returnedVars = m.call.name("FETCH_STATIC_PROP_R").inCall.name("=").filter{ assign => - val target = assign.argument.order(0).code.headOption - target.isDefined && assign.cfgNext.cfgNext.isCall.codeExact(s"RETURN ${target.get}").location.l > 0 - }.flatMap{ assign => assign.argument.order(1).isCall.argument.order(0).isLiteral.code.headOption }.l - - (returnedVars intersect fieldsHoldingConstruct).location.l > 0 - - }.size -``` - -- PRECONDITIONS: - 1. - -- TRANSFORMATION: - -``` - -``` - diff --git a/PHP/49_static_instance/README.md b/PHP/49_static_instance/README.md new file mode 100755 index 0000000..d9d8758 --- /dev/null +++ b/PHP/49_static_instance/README.md @@ -0,0 +1,141 @@ +[//]: # (This file is automatically generated. If you wish to make any changes, please use the JSON files and regenerate this file using the tpframework.) + +# Static Instance + +Tags: sast, php, php_v7.4.9 + +Version: v1.0 + +## Description + +In PHP a static variable can contain an instance of that class. + +## Overview + +| Instances | has discovery rule | discovery method | rule successfull | +|---------------------------|----------------------|--------------------|--------------------| +| [1 Instance](#1-instance) | yes | joern | no | + +## 1 Instance + +This instance shows a class, that has only one static instance. + +### Code + +```PHP +x = $b; +$a = Myclass::oneInstance()->x; +echo $a; // sink +``` + +### Instance Properties + +| category | feature_vs_internal_api | input_sanitizer | negative_test_case | source_and_sink | +|------------|---------------------------|-------------------|----------------------|-------------------| +| S0 | FEATURE | no | no | no | + +
+ +More + +
+ + +### Compile + + +```bash +$_main: + ; (lines=14, args=0, vars=2, tmps=8) + ; (before optimizer) + ; /.../PHP/49_static_instance/1_instance_49_static_instance/1_instance_49_static_instance.php:1-17 + ; return [] RANGE[0..0] +0000 T2 = FETCH_R (global) string("_GET") +0001 T3 = FETCH_DIM_R T2 string("p1") +0002 ASSIGN CV0($b) T3 +0003 INIT_STATIC_METHOD_CALL 0 string("Myclass") string("oneInstance") +0004 V5 = DO_UCALL +0005 V5 = SEPARATE V5 +0006 ASSIGN_OBJ V5 string("x") +0007 OP_DATA CV0($b) +0008 INIT_STATIC_METHOD_CALL 0 string("Myclass") string("oneInstance") +0009 V7 = DO_UCALL +0010 T8 = FETCH_OBJ_R V7 string("x") +0011 ASSIGN CV1($a) T8 +0012 ECHO CV1($a) +0013 RETURN int(1) + +Myclass::oneInstance: + ; (lines=11, args=0, vars=0, tmps=7) + ; (before optimizer) + ; /.../PHP/49_static_instance/1_instance_49_static_instance/1_instance_49_static_instance.php:6-11 + ; return [] RANGE[0..0] +0000 T0 = FETCH_STATIC_PROP_R string("instance") (self) (exception) +0001 T1 = INSTANCEOF T0 (self) (no-autoload) (silent) (exception) +0002 T2 = BOOL_NOT T1 +0003 JMPZ T2 0008 +0004 V4 = NEW 0 (self) (exception) +0005 DO_FCALL +0006 ASSIGN_STATIC_PROP string("instance") +0007 OP_DATA V4 +0008 T6 = FETCH_STATIC_PROP_R string("instance") (self) (exception) +0009 RETURN T6 +0010 RETURN null +LIVE RANGES: + 4: 0005 - 0006 (new) +``` + +
+ +
+ + +### Discovery + + +```scala +val x49 = (name, "49_static_instance", cpg.method.filter{ m => +val fieldsHoldingConstruct = m.call.name(".*__construct.*").cfgNext.isLiteral.where(_.inCall.name("ASSIGN_STATIC_PROP_1")).code.l +val returnedVars = m.call.name("FETCH_STATIC_PROP_R").inCall.name("=").filter{ assign => +val target = assign.argument.order(0).code.headOption +target.isDefined && assign.cfgNext.cfgNext.isCall.codeExact(s"RETURN ${target.get}").size > 0 +}.flatMap{ assign => assign.argument.order(1).isCall.argument.order(0).isLiteral.code.headOption }.l +(returnedVars intersect fieldsHoldingConstruct).size > 0 + +}.location.toJson); +``` + +| discovery method | expected accuracy | +|--------------------|---------------------| +| joern | FP | + +
+ +
+ + +### Measurement + + +| Tool | Comm_1 | Comm_2 | phpSAFE | Progpilot | RIPS | WAP | Ground Truth | +|-------------|----------|----------|-----------|-------------|--------|-------|----------------| +| 08 Jun 2021 | no | no | no | no | no | no | yes | +| 22 May 2023 | no | no | | | | | yes | + +
+ +
diff --git a/PHP/49_static_instance/docs/description.md b/PHP/49_static_instance/docs/description.md new file mode 100755 index 0000000..f8b2be0 --- /dev/null +++ b/PHP/49_static_instance/docs/description.md @@ -0,0 +1 @@ +In PHP a static variable can contain an instance of that class. \ No newline at end of file diff --git a/PHP/4_conditional_assignment/1_instance_4_conditional_assignment/1_instance_4_conditional_assignment.bash b/PHP/4_conditional_assignment/1_instance_4_conditional_assignment/1_instance_4_conditional_assignment.bash old mode 100644 new mode 100755 index e4435e3..4cf152b --- a/PHP/4_conditional_assignment/1_instance_4_conditional_assignment/1_instance_4_conditional_assignment.bash +++ b/PHP/4_conditional_assignment/1_instance_4_conditional_assignment/1_instance_4_conditional_assignment.bash @@ -1,22 +1,20 @@ - -$_main: ; (lines=18, args=0, vars=3, tmps=8) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/52_conditional_assignment/52_conditional_assignment.php:1-5 -L0 (2): EXT_STMT -L1 (2): ASSIGN CV0($x) int(5) -L2 (3): EXT_STMT -L3 (3): T4 = FETCH_R (global) string("_GET") -L4 (3): T5 = FETCH_DIM_R T4 string("p1") -L5 (3): ASSIGN CV1($d) T5 -L6 (4): EXT_STMT -L7 (4): T7 = IS_SMALLER int(9) CV0($x) -L8 (4): JMPZ T7 L12 -L9 (4): V8 = ASSIGN CV2($b) string("safe") -L10 (4): T9 = QM_ASSIGN V8 -L11 (4): JMP L14 -L12 (4): V10 = ASSIGN CV2($b) CV1($d) -L13 (4): T9 = QM_ASSIGN V10 -L14 (4): FREE T9 -L15 (5): EXT_STMT -L16 (5): ECHO CV2($b) -L17 (5): RETURN int(1) + +$_main: + ; (lines=14, args=0, vars=3, tmps=8) + ; (before optimizer) + ; /.../PHP/4_conditional_assignment/1_instance_4_conditional_assignment/1_instance_4_conditional_assignment.php:1-5 + ; return [] RANGE[0..0] +0000 ASSIGN CV0($c) int(5) +0001 T4 = FETCH_R (global) string("_GET") +0002 T5 = FETCH_DIM_R T4 string("p1") +0003 ASSIGN CV1($a) T5 +0004 T7 = IS_SMALLER int(9) CV0($c) +0005 JMPZ T7 0009 +0006 T8 = ASSIGN CV2($b) string("safe") +0007 T9 = QM_ASSIGN T8 +0008 JMP 0011 +0009 T10 = ASSIGN CV2($b) CV1($a) +0010 T9 = QM_ASSIGN T10 +0011 FREE T9 +0012 ECHO CV2($b) +0013 RETURN int(1) diff --git a/PHP/4_conditional_assignment/1_instance_4_conditional_assignment/1_instance_4_conditional_assignment.json b/PHP/4_conditional_assignment/1_instance_4_conditional_assignment/1_instance_4_conditional_assignment.json old mode 100644 new mode 100755 index 1e9f779..152dfe2 --- a/PHP/4_conditional_assignment/1_instance_4_conditional_assignment/1_instance_4_conditional_assignment.json +++ b/PHP/4_conditional_assignment/1_instance_4_conditional_assignment/1_instance_4_conditional_assignment.json @@ -1,37 +1,38 @@ -{ - "code": { - "path": "./1_instance_4_conditional_assignment.php", - "injection_skeleton_broken": false - }, - "discovery": { - "rule": "./1_instance_4_conditional_assignment.sc", - "method": "joern", - "rule_accuracy": "Perfect", - "notes": "This rule should discover all conditional assignment" - }, - "remediation": { - "notes": "./docs/remediation_notes.md", - "transformation": null, - "modeling_rule": null - }, - "compile": { - "binary": "./1_instance_4_conditional_assignment.bash", - "dependencies": null, - "instruction": null - }, - "expectation": { - "type": "xss", - "sink_file": "./1_instance_4_conditional_assignment.php", - "sink_line": 5, - "source_file": "./1_instance_4_conditional_assignment.php", - "source_line": 2, - "expectation": true - }, - "properties": { - "category": "S0", - "feature_vs_internal_api": "FEATURE", - "input_sanitizer": false, - "source_and_sink": false, - "negative_test_case": false - } +{ + "description": "This instance captures the conditional assignment of a variable using the Question Mark Operator.", + "code": { + "path": "./1_instance_4_conditional_assignment.php", + "injection_skeleton_broken": false + }, + "discovery": { + "rule": "./1_instance_4_conditional_assignment.sc", + "method": "joern", + "rule_accuracy": "Perfect", + "notes": "The rule searches for `QM_ASSIGN`, which stands for Question Mark Assign. This rule should discover all conditional assignments." + }, + "remediation": { + "notes": "./docs/remediation_notes.md", + "transformation": null, + "modeling_rule": null + }, + "compile": { + "binary": "./1_instance_4_conditional_assignment.bash", + "dependencies": null, + "instruction": null + }, + "expectation": { + "type": "xss", + "sink_file": "./1_instance_4_conditional_assignment.php", + "sink_line": 5, + "source_file": "./1_instance_4_conditional_assignment.php", + "source_line": 3, + "expectation": true + }, + "properties": { + "category": "S0", + "feature_vs_internal_api": "FEATURE", + "input_sanitizer": false, + "source_and_sink": false, + "negative_test_case": false + } } \ No newline at end of file diff --git a/PHP/4_conditional_assignment/1_instance_4_conditional_assignment/1_instance_4_conditional_assignment.php b/PHP/4_conditional_assignment/1_instance_4_conditional_assignment/1_instance_4_conditional_assignment.php old mode 100644 new mode 100755 index 92da242..3a2f847 --- a/PHP/4_conditional_assignment/1_instance_4_conditional_assignment/1_instance_4_conditional_assignment.php +++ b/PHP/4_conditional_assignment/1_instance_4_conditional_assignment/1_instance_4_conditional_assignment.php @@ -1,5 +1,5 @@ - 9? $b = "safe" : $b = $a; // tarpit + 9? $b = "safe" : $b = $a; // tarpit echo $b; // sink \ No newline at end of file diff --git a/PHP/4_conditional_assignment/1_instance_4_conditional_assignment/1_instance_4_conditional_assignment.sc b/PHP/4_conditional_assignment/1_instance_4_conditional_assignment/1_instance_4_conditional_assignment.sc old mode 100644 new mode 100755 index 2a857b5..00b9ba2 --- a/PHP/4_conditional_assignment/1_instance_4_conditional_assignment/1_instance_4_conditional_assignment.sc +++ b/PHP/4_conditional_assignment/1_instance_4_conditional_assignment/1_instance_4_conditional_assignment.sc @@ -1,6 +1,6 @@ -@main def main(name : String): Unit = { - importCpg(name) - val x4 = (name, "4_conditional_assignment_iall", cpg.call(".*QM_ASSIGN.*").location.toJson); - println(x4) - delete; +@main def main(name : String): Unit = { + importCpg(name) + val x4 = (name, "4_conditional_assignment_iall", cpg.call(".*QM_ASSIGN.*").location.toJson); + println(x4) + delete; } \ No newline at end of file diff --git a/PHP/4_conditional_assignment/1_instance_4_conditional_assignment/docs/remediation_notes.md b/PHP/4_conditional_assignment/1_instance_4_conditional_assignment/docs/remediation_notes.md old mode 100644 new mode 100755 index 71a45de..1f0a6ae --- a/PHP/4_conditional_assignment/1_instance_4_conditional_assignment/docs/remediation_notes.md +++ b/PHP/4_conditional_assignment/1_instance_4_conditional_assignment/docs/remediation_notes.md @@ -1,3 +1,3 @@ -- Can we transform this tarpit in a standard if-then-else that may be easier to be treated by SAST tools? - +- Can we transform this tarpit in a standard if-then-else that may be easier to be treated by SAST tools? + - Can we create a modeling rule for such a syntactic construct? \ No newline at end of file diff --git a/PHP/4_conditional_assignment/4_conditional_assignment.json b/PHP/4_conditional_assignment/4_conditional_assignment.json old mode 100644 new mode 100755 index 2b6dba7..1c4d46a --- a/PHP/4_conditional_assignment/4_conditional_assignment.json +++ b/PHP/4_conditional_assignment/4_conditional_assignment.json @@ -1,14 +1,14 @@ -{ - "name": "Conditional Assignment", - "description": "", - "family": "code_pattern_php", - "tags": [ - "sast", - "php", - "php_v7.4.9" - ], - "instances": [ - "./1_instance_4_conditional_assignment/1_instance_4_conditional_assignment.json" - ], - "version": "v0.draft" +{ + "name": "Conditional Assignment", + "description": "./docs/description.md", + "family": "code_pattern_php", + "tags": [ + "sast", + "php", + "php_v7.4.9" + ], + "instances": [ + "./1_instance_4_conditional_assignment/1_instance_4_conditional_assignment.json" + ], + "version": "v1.0" } \ No newline at end of file diff --git a/PHP/4_conditional_assignment/README.md b/PHP/4_conditional_assignment/README.md old mode 100644 new mode 100755 index d40f7fc..330c4b8 --- a/PHP/4_conditional_assignment/README.md +++ b/PHP/4_conditional_assignment/README.md @@ -1,74 +1,116 @@ -# Pattern: Conditional Assignment - -## Category - -Variables - -## Definition - -## Instances - -### Instance 1 - -- CATEGORY: S0 -- FEATURE vs INTERNAL API: FEATURE -- INPUT SANITIZERS: NO -- SOURCES AND SINKS: NO -- NEGATIVE TEST CASES: NO -- CODE: - -```php - 9? $b = "safe" : $b = $d; -echo $b; -``` - -- MEASUREMENT: - -| Tool | RIPS | phpSAFE | WAP | Progpilot | Comm_1 | Comm_2 | Correct | -| ------------- | ---- | ------- | ---- | --------- | ------- | --------- | ------- | -| Vulnerability | YES | NO | YES | YES | YES | YES | YES | -Measurements Date: 8 June 2021 - -- OPCODE: - -```bash -$_main: ; (lines=18, args=0, vars=3, tmps=8) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/52_conditional_assignment/52_conditional_assignment.php:1-5 -L0 (2): EXT_STMT -L1 (2): ASSIGN CV0($x) int(5) -L2 (3): EXT_STMT -L3 (3): T4 = FETCH_R (global) string("_GET") -L4 (3): T5 = FETCH_DIM_R T4 string("p1") -L5 (3): ASSIGN CV1($d) T5 -L6 (4): EXT_STMT -L7 (4): T7 = IS_SMALLER int(9) CV0($x) -L8 (4): JMPZ T7 L12 -L9 (4): V8 = ASSIGN CV2($b) string("safe") -L10 (4): T9 = QM_ASSIGN V8 -L11 (4): JMP L14 -L12 (4): V10 = ASSIGN CV2($b) CV1($d) -L13 (4): T9 = QM_ASSIGN V10 -L14 (4): FREE T9 -L15 (5): EXT_STMT -L16 (5): ECHO CV2($b) -L17 (5): RETURN int(1) -``` - -- DISCOVERY: - -```bash -cpg.call(".*QM_ASSIGN.*").location.l -``` - -- PRECONDITIONS: - 1. -- TRANSFORMATION: - -``` - -``` - +[//]: # (This file is automatically generated. If you wish to make any changes, please use the JSON files and regenerate this file using the tpframework.) + +# Conditional Assignment + +Tags: sast, php, php_v7.4.9 + +Version: v1.0 + +## Description + +The ternary operator in PHP is a shorthand way of writing an `if-else` statement that returns one of two values based on a given condition. + +## Overview + +| Instances | has discovery rule | discovery method | rule successfull | +|---------------------------|----------------------|--------------------|--------------------| +| [1 Instance](#1-instance) | yes | joern | yes | + +## 1 Instance + +This instance captures the conditional assignment of a variable using the Question Mark Operator. + +### Code + +```PHP + 9? $b = "safe" : $b = $a; // tarpit +echo $b; // sink +``` + +### Instance Properties + +| category | feature_vs_internal_api | input_sanitizer | negative_test_case | source_and_sink | +|------------|---------------------------|-------------------|----------------------|-------------------| +| S0 | FEATURE | no | no | no | + +
+ +More + +
+ + +### Compile + + +```bash +$_main: + ; (lines=14, args=0, vars=3, tmps=8) + ; (before optimizer) + ; /.../PHP/4_conditional_assignment/1_instance_4_conditional_assignment/1_instance_4_conditional_assignment.php:1-5 + ; return [] RANGE[0..0] +0000 ASSIGN CV0($c) int(5) +0001 T4 = FETCH_R (global) string("_GET") +0002 T5 = FETCH_DIM_R T4 string("p1") +0003 ASSIGN CV1($a) T5 +0004 T7 = IS_SMALLER int(9) CV0($c) +0005 JMPZ T7 0009 +0006 T8 = ASSIGN CV2($b) string("safe") +0007 T9 = QM_ASSIGN T8 +0008 JMP 0011 +0009 T10 = ASSIGN CV2($b) CV1($a) +0010 T9 = QM_ASSIGN T10 +0011 FREE T9 +0012 ECHO CV2($b) +0013 RETURN int(1) +``` + +
+ +
+ + +### Discovery + + +The rule searches for `QM_ASSIGN`, which stands for Question Mark Assign. This rule should discover all conditional assignments. + +```scala +val x4 = (name, "4_conditional_assignment_iall", cpg.call(".*QM_ASSIGN.*").location.toJson); +``` + +| discovery method | expected accuracy | +|--------------------|---------------------| +| joern | Perfect | + +
+ +
+ + +### Measurement + + +| Tool | Comm_1 | Comm_2 | phpSAFE | Progpilot | RIPS | WAP | Ground Truth | +|-------------|----------|----------|-----------|-------------|--------|-------|----------------| +| 08 Jun 2021 | yes | yes | no | yes | yes | yes | yes | +| 17 May 2023 | yes | yes | | | | | yes | + +
+ +
+ + +### Remediation + + +- Can we transform this tarpit in a standard if-then-else that may be easier to be treated by SAST tools? + +- Can we create a modeling rule for such a syntactic construct? + +
+ +
diff --git a/PHP/4_conditional_assignment/docs/description.md b/PHP/4_conditional_assignment/docs/description.md new file mode 100755 index 0000000..d56d262 --- /dev/null +++ b/PHP/4_conditional_assignment/docs/description.md @@ -0,0 +1 @@ +The ternary operator in PHP is a shorthand way of writing an `if-else` statement that returns one of two values based on a given condition. \ No newline at end of file diff --git a/PHP/50_throw_exception/1_instance_50_throw_exception/1_instance_50_throw_exception.bash b/PHP/50_throw_exception/1_instance_50_throw_exception/1_instance_50_throw_exception.bash old mode 100644 new mode 100755 index ea68cb7..b36d60a --- a/PHP/50_throw_exception/1_instance_50_throw_exception/1_instance_50_throw_exception.bash +++ b/PHP/50_throw_exception/1_instance_50_throw_exception/1_instance_50_throw_exception.bash @@ -1,56 +1,47 @@ - -$_main: ; (lines=26, args=0, vars=2, tmps=6) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/40_throw_exception/40_throw_exception.php:1-16 -L0 (9): EXT_STMT -L1 (9): T2 = FETCH_R (global) string("_GET") -L2 (9): T3 = FETCH_DIM_R T2 string("p1") -L3 (9): ASSIGN CV0($b) T3 -L4 (10): NOP -L5 (11): EXT_STMT -L6 (11): INIT_FCALL 2 176 string("inverse") -L7 (11): SEND_VAL int(5) 1 -L8 (11): SEND_VAR CV0($b) 2 -L9 (11): DO_FCALL -L10 (12): EXT_STMT -L11 (12): INIT_FCALL 2 176 string("inverse") -L12 (12): SEND_VAL int(0) 1 -L13 (12): SEND_VAR CV0($b) 2 -L14 (12): DO_FCALL -L15 (12): JMP L25 -L16 (13): CV1($e) = CATCH string("Exception") -L17 (15): EXT_STMT -L18 (15): ECHO string("Caught exception: ") -L19 (15): EXT_STMT -L20 (15): INIT_METHOD_CALL 0 CV1($e) string("getMessage") -L21 (15): V7 = DO_FCALL -L22 (15): ECHO V7 -L23 (15): EXT_STMT -L24 (15): ECHO string(" -") -L25 (16): RETURN int(1) -EXCEPTION TABLE: - L5, L16, -, - - -inverse: ; (lines=16, args=2, vars=2, tmps=4) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/40_throw_exception/40_throw_exception.php:2-7 -L0 (2): EXT_NOP -L1 (2): CV0($x) = RECV 1 -L2 (2): CV1($b) = RECV 2 -L3 (3): EXT_STMT -L4 (3): T2 = BOOL_NOT CV0($x) -L5 (3): JMPZ T2 L11 -L6 (4): EXT_STMT -L7 (4): V3 = NEW 1 string("Exception") -L8 (4): SEND_VAR_EX CV1($b) 1 -L9 (4): DO_FCALL -L10 (4): THROW V3 -L11 (6): EXT_STMT -L12 (6): T5 = DIV int(1) CV0($x) -L13 (6): RETURN T5 -L14 (7): EXT_STMT -L15 (7): RETURN null -LIVE RANGES: - 3: L8 - L10 (new) - + +$_main: + ; (lines=19, args=0, vars=2, tmps=6) + ; (before optimizer) + ; /.../PHP/50_throw_exception/1_instance_50_throw_exception/1_instance_50_throw_exception.php:1-16 + ; return [] RANGE[0..0] +0000 T2 = FETCH_R (global) string("_GET") +0001 T3 = FETCH_DIM_R T2 string("p1") +0002 ASSIGN CV0($b) T3 +0003 INIT_FCALL 2 176 string("inverse") +0004 SEND_VAL int(5) 1 +0005 SEND_VAR CV0($b) 2 +0006 DO_UCALL +0007 INIT_FCALL 2 176 string("inverse") +0008 SEND_VAL int(0) 1 +0009 SEND_VAR CV0($b) 2 +0010 DO_UCALL +0011 JMP 0018 +0012 CV1($e) = CATCH string("Exception") +0013 ECHO string("Caught exception: ") +0014 INIT_METHOD_CALL 0 CV1($e) string("getMessage") +0015 V7 = DO_FCALL +0016 ECHO V7 +0017 ECHO string(" +") +0018 RETURN int(1) +EXCEPTION TABLE: + 0003, 0012, -, - + +inverse: + ; (lines=11, args=2, vars=2, tmps=4) + ; (before optimizer) + ; /.../PHP/50_throw_exception/1_instance_50_throw_exception/1_instance_50_throw_exception.php:2-7 + ; return [] RANGE[0..0] +0000 CV0($x) = RECV 1 +0001 CV1($b) = RECV 2 +0002 T2 = BOOL_NOT CV0($x) +0003 JMPZ T2 0008 +0004 V3 = NEW 1 string("Exception") +0005 SEND_VAR_EX CV1($b) 1 +0006 DO_FCALL +0007 THROW V3 +0008 T5 = DIV int(1) CV0($x) +0009 RETURN T5 +0010 RETURN null +LIVE RANGES: + 3: 0005 - 0007 (new) diff --git a/PHP/50_throw_exception/1_instance_50_throw_exception/1_instance_50_throw_exception.json b/PHP/50_throw_exception/1_instance_50_throw_exception/1_instance_50_throw_exception.json old mode 100644 new mode 100755 index abf9d37..8ed329a --- a/PHP/50_throw_exception/1_instance_50_throw_exception/1_instance_50_throw_exception.json +++ b/PHP/50_throw_exception/1_instance_50_throw_exception/1_instance_50_throw_exception.json @@ -1,37 +1,38 @@ -{ - "code": { - "path": "./1_instance_50_throw_exception.php", - "injection_skeleton_broken": false - }, - "discovery": { - "rule": "./1_instance_50_throw_exception.sc", - "method": "joern", - "rule_accuracy": null, - "notes": null - }, - "compile": { - "binary": "./1_instance_50_throw_exception.bash", - "instruction": null, - "dependencies": null - }, - "expectation": { - "type": "xss", - "sink_file": "./1_instance_50_throw_exception.php", - "sink_line": 15, - "source_file": "./1_instance_50_throw_exception.php", - "source_line": 9, - "expectation": true - }, - "properties": { - "category": "D2", - "feature_vs_internal_api": "FEATURE", - "input_sanitizer": false, - "source_and_sink": false, - "negative_test_case": false - }, - "remediation": { - "notes": "", - "transformation": null, - "modeling_rule": null - } +{ + "description": "This instance shows a simple example for throwing and catching an exception.", + "code": { + "path": "./1_instance_50_throw_exception.php", + "injection_skeleton_broken": false + }, + "discovery": { + "rule": "./1_instance_50_throw_exception.sc", + "method": "joern", + "rule_accuracy": "Perfect", + "notes": "The rule searches for a call for `THROW` on opcode level." + }, + "compile": { + "binary": "./1_instance_50_throw_exception.bash", + "instruction": null, + "dependencies": null + }, + "expectation": { + "type": "xss", + "sink_file": "./1_instance_50_throw_exception.php", + "sink_line": 15, + "source_file": "./1_instance_50_throw_exception.php", + "source_line": 9, + "expectation": true + }, + "properties": { + "category": "D2", + "feature_vs_internal_api": "FEATURE", + "input_sanitizer": false, + "source_and_sink": false, + "negative_test_case": false + }, + "remediation": { + "notes": "", + "transformation": null, + "modeling_rule": null + } } \ No newline at end of file diff --git a/PHP/50_throw_exception/1_instance_50_throw_exception/1_instance_50_throw_exception.php b/PHP/50_throw_exception/1_instance_50_throw_exception/1_instance_50_throw_exception.php old mode 100644 new mode 100755 index 6cfd945..7ee167e --- a/PHP/50_throw_exception/1_instance_50_throw_exception/1_instance_50_throw_exception.php +++ b/PHP/50_throw_exception/1_instance_50_throw_exception/1_instance_50_throw_exception.php @@ -1,16 +1,16 @@ -getMessage(), "\n"; +getMessage(), "\n"; // sink } \ No newline at end of file diff --git a/PHP/50_throw_exception/1_instance_50_throw_exception/1_instance_50_throw_exception.sc b/PHP/50_throw_exception/1_instance_50_throw_exception/1_instance_50_throw_exception.sc old mode 100644 new mode 100755 index c9ac781..d05c127 --- a/PHP/50_throw_exception/1_instance_50_throw_exception/1_instance_50_throw_exception.sc +++ b/PHP/50_throw_exception/1_instance_50_throw_exception/1_instance_50_throw_exception.sc @@ -1,6 +1,6 @@ -@main def main(name : String): Unit = { - importCpg(name) - val x50 = (name, "50_throw_exception_iall", cpg.call(".*THROW.*").location.toJson); - println(x50) - delete; +@main def main(name : String): Unit = { + importCpg(name) + val x50 = (name, "50_throw_exception_iall", cpg.call(".*THROW.*").location.toJson); + println(x50) + delete; } \ No newline at end of file diff --git a/PHP/50_throw_exception/50_throw_exception.json b/PHP/50_throw_exception/50_throw_exception.json old mode 100644 new mode 100755 index bd293be..0826142 --- a/PHP/50_throw_exception/50_throw_exception.json +++ b/PHP/50_throw_exception/50_throw_exception.json @@ -1,14 +1,14 @@ -{ - "name": "Throw Exception", - "description": "", - "family": "code_pattern_php", - "tags": [ - "sast", - "php", - "php_v7.4.9" - ], - "instances": [ - "./1_instance_50_throw_exception/1_instance_50_throw_exception.json" - ], - "version": "v0.draft" +{ + "name": "Throw Exception", + "description": "./docs/description.md", + "family": "code_pattern_php", + "tags": [ + "sast", + "php", + "php_v7.4.9" + ], + "instances": [ + "./1_instance_50_throw_exception/1_instance_50_throw_exception.json" + ], + "version": "v1.0" } \ No newline at end of file diff --git a/PHP/50_throw_exception/Pattern Throw Exceptions.md b/PHP/50_throw_exception/Pattern Throw Exceptions.md deleted file mode 100644 index 8ec9513..0000000 --- a/PHP/50_throw_exception/Pattern Throw Exceptions.md +++ /dev/null @@ -1,119 +0,0 @@ -# Pattern: Throw Exceptions - -## Category - -Exceptions - -## Definition - -## Instances - -### Instance 1 - -- CATEGORY: D2 -- FEATURE vs INTERNAL API: FEATURE -- INPUT SANITIZERS: NO -- SOURCES AND SINKS: NO -- NEGATIVE TEST CASES: NO -- CODE: - -```php -getMessage(), "\n"; -} -``` - -- MEASUREMENT: - -| Tool | RIPS | phpSAFE | WAP | Progpilot | Comm_1 | Comm_2 | Correct | -| ------------- | ---- | ------- | ---- | --------- | ------- | --------- | ------- | -| Vulnerability | NO | NO | NO | NO | NO | NO | YES | -Measurements Date: 8 June 2021 - -- OPCODE: - -```bash -$_main: ; (lines=26, args=0, vars=2, tmps=6) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/40_throw_exception/40_throw_exception.php:1-16 -L0 (9): EXT_STMT -L1 (9): T2 = FETCH_R (global) string("_GET") -L2 (9): T3 = FETCH_DIM_R T2 string("p1") -L3 (9): ASSIGN CV0($b) T3 -L4 (10): NOP -L5 (11): EXT_STMT -L6 (11): INIT_FCALL 2 176 string("inverse") -L7 (11): SEND_VAL int(5) 1 -L8 (11): SEND_VAR CV0($b) 2 -L9 (11): DO_FCALL -L10 (12): EXT_STMT -L11 (12): INIT_FCALL 2 176 string("inverse") -L12 (12): SEND_VAL int(0) 1 -L13 (12): SEND_VAR CV0($b) 2 -L14 (12): DO_FCALL -L15 (12): JMP L25 -L16 (13): CV1($e) = CATCH string("Exception") -L17 (15): EXT_STMT -L18 (15): ECHO string("Caught exception: ") -L19 (15): EXT_STMT -L20 (15): INIT_METHOD_CALL 0 CV1($e) string("getMessage") -L21 (15): V7 = DO_FCALL -L22 (15): ECHO V7 -L23 (15): EXT_STMT -L24 (15): ECHO string(" -") -L25 (16): RETURN int(1) -EXCEPTION TABLE: - L5, L16, -, - - -inverse: ; (lines=16, args=2, vars=2, tmps=4) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/40_throw_exception/40_throw_exception.php:2-7 -L0 (2): EXT_NOP -L1 (2): CV0($x) = RECV 1 -L2 (2): CV1($b) = RECV 2 -L3 (3): EXT_STMT -L4 (3): T2 = BOOL_NOT CV0($x) -L5 (3): JMPZ T2 L11 -L6 (4): EXT_STMT -L7 (4): V3 = NEW 1 string("Exception") -L8 (4): SEND_VAR_EX CV1($b) 1 -L9 (4): DO_FCALL -L10 (4): THROW V3 -L11 (6): EXT_STMT -L12 (6): T5 = DIV int(1) CV0($x) -L13 (6): RETURN T5 -L14 (7): EXT_STMT -L15 (7): RETURN null -LIVE RANGES: - 3: L8 - L10 (new) -``` - -- DISCOVERY: - -```bash -cpg.call(".*THROW.*").location.l -``` - -- PRECONDITIONS: - 1. - -- TRANSFORMATION: T3 - -``` - -``` - diff --git a/PHP/50_throw_exception/README.md b/PHP/50_throw_exception/README.md new file mode 100755 index 0000000..ac97c4a --- /dev/null +++ b/PHP/50_throw_exception/README.md @@ -0,0 +1,142 @@ +[//]: # (This file is automatically generated. If you wish to make any changes, please use the JSON files and regenerate this file using the tpframework.) + +# Throw Exception + +Tags: sast, php, php_v7.4.9 + +Version: v1.0 + +## Description + +In PHP [Exceptions](https://www.php.net/manual/en/language.exceptions.php) are similar to other languages. An exception can be thrown and caught, try blocks indicate positions in code, where exceptions can occur. This pattern should target throwing an exception + +## Overview + +| Instances | has discovery rule | discovery method | rule successfull | +|---------------------------|----------------------|--------------------|--------------------| +| [1 Instance](#1-instance) | yes | joern | yes | + +## 1 Instance + +This instance shows a simple example for throwing and catching an exception. + +### Code + +```PHP +getMessage(), "\n"; // sink +} +``` + +### Instance Properties + +| category | feature_vs_internal_api | input_sanitizer | negative_test_case | source_and_sink | +|------------|---------------------------|-------------------|----------------------|-------------------| +| D2 | FEATURE | no | no | no | + +
+ +More + +
+ + +### Compile + + +```bash +$_main: + ; (lines=19, args=0, vars=2, tmps=6) + ; (before optimizer) + ; /.../PHP/50_throw_exception/1_instance_50_throw_exception/1_instance_50_throw_exception.php:1-16 + ; return [] RANGE[0..0] +0000 T2 = FETCH_R (global) string("_GET") +0001 T3 = FETCH_DIM_R T2 string("p1") +0002 ASSIGN CV0($b) T3 +0003 INIT_FCALL 2 176 string("inverse") +0004 SEND_VAL int(5) 1 +0005 SEND_VAR CV0($b) 2 +0006 DO_UCALL +0007 INIT_FCALL 2 176 string("inverse") +0008 SEND_VAL int(0) 1 +0009 SEND_VAR CV0($b) 2 +0010 DO_UCALL +0011 JMP 0018 +0012 CV1($e) = CATCH string("Exception") +0013 ECHO string("Caught exception: ") +0014 INIT_METHOD_CALL 0 CV1($e) string("getMessage") +0015 V7 = DO_FCALL +0016 ECHO V7 +0017 ECHO string(" +") +0018 RETURN int(1) +EXCEPTION TABLE: + 0003, 0012, -, - + +inverse: + ; (lines=11, args=2, vars=2, tmps=4) + ; (before optimizer) + ; /.../PHP/50_throw_exception/1_instance_50_throw_exception/1_instance_50_throw_exception.php:2-7 + ; return [] RANGE[0..0] +0000 CV0($x) = RECV 1 +0001 CV1($b) = RECV 2 +0002 T2 = BOOL_NOT CV0($x) +0003 JMPZ T2 0008 +0004 V3 = NEW 1 string("Exception") +0005 SEND_VAR_EX CV1($b) 1 +0006 DO_FCALL +0007 THROW V3 +0008 T5 = DIV int(1) CV0($x) +0009 RETURN T5 +0010 RETURN null +LIVE RANGES: + 3: 0005 - 0007 (new) +``` + +
+ +
+ + +### Discovery + + +The rule searches for a call for `THROW` on opcode level. + +```scala +val x50 = (name, "50_throw_exception_iall", cpg.call(".*THROW.*").location.toJson); +``` + +| discovery method | expected accuracy | +|--------------------|---------------------| +| joern | Perfect | + +
+ +
+ + +### Measurement + + +| Tool | Comm_1 | Comm_2 | phpSAFE | Progpilot | RIPS | WAP | Ground Truth | +|-------------|----------|----------|-----------|-------------|--------|-------|----------------| +| 08 Jun 2021 | no | no | no | no | no | no | yes | +| 22 May 2023 | no | no | | | | | yes | + +
+ +
diff --git a/PHP/50_throw_exception/docs/description.md b/PHP/50_throw_exception/docs/description.md new file mode 100755 index 0000000..9bfbf79 --- /dev/null +++ b/PHP/50_throw_exception/docs/description.md @@ -0,0 +1 @@ +In PHP [Exceptions](https://www.php.net/manual/en/language.exceptions.php) are similar to other languages. An exception can be thrown and caught, try blocks indicate positions in code, where exceptions can occur. This pattern should target throwing an exception \ No newline at end of file diff --git a/PHP/51_catch_exceptions/1_instance_51_catch_exceptions/1_instance_51_catch_exception.bash b/PHP/51_catch_exceptions/1_instance_51_catch_exceptions/1_instance_51_catch_exception.bash deleted file mode 100644 index 1a30640..0000000 --- a/PHP/51_catch_exceptions/1_instance_51_catch_exceptions/1_instance_51_catch_exception.bash +++ /dev/null @@ -1,57 +0,0 @@ - -$_main: ; (lines=28, args=0, vars=2, tmps=6) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/41_catch_exceptions/41_catch_exception.php:1-17 -L0 (9): EXT_STMT -L1 (9): T2 = FETCH_R (global) string("_GET") -L2 (9): T3 = FETCH_DIM_R T2 string("p1") -L3 (9): ASSIGN CV0($b) T3 -L4 (10): NOP -L5 (11): EXT_STMT -L6 (11): INIT_FCALL 2 176 string("inverse") -L7 (11): SEND_VAL int(5) 1 -L8 (11): SEND_VAR CV0($b) 2 -L9 (11): DO_FCALL -L10 (12): EXT_STMT -L11 (12): INIT_FCALL 2 176 string("inverse") -L12 (12): SEND_VAL int(0) 1 -L13 (12): SEND_VAR CV0($b) 2 -L14 (12): DO_FCALL -L15 (12): JMP L27 -L16 (13): CV1($e) = CATCH string("Exception") -L17 (15): EXT_STMT -L18 (15): ECHO CV0($b) -L19 (16): EXT_STMT -L20 (16): ECHO string("Caught exception: ") -L21 (16): EXT_STMT -L22 (16): INIT_METHOD_CALL 0 CV1($e) string("getMessage") -L23 (16): V7 = DO_FCALL -L24 (16): ECHO V7 -L25 (16): EXT_STMT -L26 (16): ECHO string(" -") -L27 (17): RETURN int(1) -EXCEPTION TABLE: - L5, L16, -, - - -inverse: ; (lines=16, args=2, vars=2, tmps=4) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/41_catch_exceptions/41_catch_exception.php:2-7 -L0 (2): EXT_NOP -L1 (2): CV0($x) = RECV 1 -L2 (2): CV1($b) = RECV 2 -L3 (3): EXT_STMT -L4 (3): T2 = BOOL_NOT CV0($x) -L5 (3): JMPZ T2 L11 -L6 (4): EXT_STMT -L7 (4): V3 = NEW 1 string("Exception") -L8 (4): SEND_VAR_EX CV1($b) 1 -L9 (4): DO_FCALL -L10 (4): THROW V3 -L11 (6): EXT_STMT -L12 (6): T5 = DIV int(1) CV0($x) -L13 (6): RETURN T5 -L14 (7): EXT_STMT -L15 (7): RETURN null -LIVE RANGES: - 3: L8 - L10 (new) \ No newline at end of file diff --git a/PHP/51_catch_exceptions/1_instance_51_catch_exceptions/1_instance_51_catch_exceptions.bash b/PHP/51_catch_exceptions/1_instance_51_catch_exceptions/1_instance_51_catch_exceptions.bash new file mode 100755 index 0000000..ec3368f --- /dev/null +++ b/PHP/51_catch_exceptions/1_instance_51_catch_exceptions/1_instance_51_catch_exceptions.bash @@ -0,0 +1,48 @@ + +$_main: + ; (lines=20, args=0, vars=2, tmps=6) + ; (before optimizer) + ; /.../PHP/51_catch_exceptions/1_instance_51_catch_exceptions/1_instance_51_catch_exceptions.php:1-18 + ; return [] RANGE[0..0] +0000 T2 = FETCH_R (global) string("_GET") +0001 T3 = FETCH_DIM_R T2 string("p1") +0002 ASSIGN CV0($b) T3 +0003 INIT_FCALL 2 176 string("inverse") +0004 SEND_VAL int(5) 1 +0005 SEND_VAR CV0($b) 2 +0006 DO_UCALL +0007 INIT_FCALL 2 176 string("inverse") +0008 SEND_VAL int(0) 1 +0009 SEND_VAR CV0($b) 2 +0010 DO_UCALL +0011 JMP 0019 +0012 CV1($e) = CATCH string("Exception") +0013 ECHO CV0($b) +0014 ECHO string("Caught exception: ") +0015 INIT_METHOD_CALL 0 CV1($e) string("getMessage") +0016 V7 = DO_FCALL +0017 ECHO V7 +0018 ECHO string(" +") +0019 RETURN int(1) +EXCEPTION TABLE: + 0003, 0012, -, - + +inverse: + ; (lines=11, args=2, vars=2, tmps=4) + ; (before optimizer) + ; /.../PHP/51_catch_exceptions/1_instance_51_catch_exceptions/1_instance_51_catch_exceptions.php:2-7 + ; return [] RANGE[0..0] +0000 CV0($x) = RECV 1 +0001 CV1($b) = RECV 2 +0002 T2 = BOOL_NOT CV0($x) +0003 JMPZ T2 0008 +0004 V3 = NEW 1 string("Exception") +0005 SEND_VAR_EX CV1($b) 1 +0006 DO_FCALL +0007 THROW V3 +0008 T5 = DIV int(1) CV0($x) +0009 RETURN T5 +0010 RETURN null +LIVE RANGES: + 3: 0005 - 0007 (new) diff --git a/PHP/51_catch_exceptions/1_instance_51_catch_exceptions/1_instance_51_catch_exceptions.json b/PHP/51_catch_exceptions/1_instance_51_catch_exceptions/1_instance_51_catch_exceptions.json old mode 100644 new mode 100755 index a73faad..8106fe1 --- a/PHP/51_catch_exceptions/1_instance_51_catch_exceptions/1_instance_51_catch_exceptions.json +++ b/PHP/51_catch_exceptions/1_instance_51_catch_exceptions/1_instance_51_catch_exceptions.json @@ -1,37 +1,38 @@ -{ - "code": { - "path": "./1_instance_51_catch_exceptions.php", - "injection_skeleton_broken": false - }, - "discovery": { - "rule": "./1_instance_51_catch_exceptions.sc", - "method": "joern", - "rule_accuracy": null, - "notes": null - }, - "compile": { - "binary": "./1_instance_51_catch_exceptions.bash", - "instruction": null, - "dependencies": null - }, - "expectation": { - "type": "xss", - "sink_file": "./1_instance_51_catch_exceptions.php", - "sink_line": 16, - "source_file": "./1_instance_51_catch_exceptions.php", - "source_line": 9, - "expectation": true - }, - "properties": { - "category": "S0", - "feature_vs_internal_api": "FEATURE", - "input_sanitizer": false, - "source_and_sink": false, - "negative_test_case": false - }, - "remediation": { - "notes": "", - "transformation": null, - "modeling_rule": null - } +{ + "description": "This instance focuses on the catch part of the exception.", + "code": { + "path": "./1_instance_51_catch_exceptions.php", + "injection_skeleton_broken": true + }, + "discovery": { + "rule": "./1_instance_51_catch_exceptions.sc", + "method": "joern", + "rule_accuracy": "Perfect", + "notes": "The rule searches for all calls to `CATCH` on opcode level." + }, + "compile": { + "binary": "./1_instance_51_catch_exceptions.bash", + "instruction": null, + "dependencies": null + }, + "expectation": { + "type": "xss", + "sink_file": "./1_instance_51_catch_exceptions.php", + "sink_line": 15, + "source_file": "./1_instance_51_catch_exceptions.php", + "source_line": 9, + "expectation": true + }, + "properties": { + "category": "S0", + "feature_vs_internal_api": "FEATURE", + "input_sanitizer": false, + "source_and_sink": false, + "negative_test_case": false + }, + "remediation": { + "notes": "", + "transformation": null, + "modeling_rule": null + } } \ No newline at end of file diff --git a/PHP/51_catch_exceptions/1_instance_51_catch_exceptions/1_instance_51_catch_exception.php b/PHP/51_catch_exceptions/1_instance_51_catch_exceptions/1_instance_51_catch_exceptions.php old mode 100644 new mode 100755 similarity index 78% rename from PHP/51_catch_exceptions/1_instance_51_catch_exceptions/1_instance_51_catch_exception.php rename to PHP/51_catch_exceptions/1_instance_51_catch_exceptions/1_instance_51_catch_exceptions.php index 44d32cf..7960eee --- a/PHP/51_catch_exceptions/1_instance_51_catch_exceptions/1_instance_51_catch_exception.php +++ b/PHP/51_catch_exceptions/1_instance_51_catch_exceptions/1_instance_51_catch_exceptions.php @@ -1,17 +1,17 @@ -getMessage(), "\n"; -} \ No newline at end of file +getMessage(), "\n"; +} diff --git a/PHP/51_catch_exceptions/1_instance_51_catch_exceptions/1_instance_51_catch_exceptions.sc b/PHP/51_catch_exceptions/1_instance_51_catch_exceptions/1_instance_51_catch_exceptions.sc old mode 100644 new mode 100755 index de9677d..229a1c0 --- a/PHP/51_catch_exceptions/1_instance_51_catch_exceptions/1_instance_51_catch_exceptions.sc +++ b/PHP/51_catch_exceptions/1_instance_51_catch_exceptions/1_instance_51_catch_exceptions.sc @@ -1,6 +1,6 @@ -@main def main(name : String): Unit = { - importCpg(name) - val x51 = (name, "51_catch_exception_iall", cpg.call(".*CATCH.*").location.toJson); - println(x51) - delete; +@main def main(name : String): Unit = { + importCpg(name) + val x51 = (name, "51_catch_exception_iall", cpg.call(".*CATCH.*").location.toJson); + println(x51) + delete; } \ No newline at end of file diff --git a/PHP/51_catch_exceptions/51_catch_exceptions.json b/PHP/51_catch_exceptions/51_catch_exceptions.json old mode 100644 new mode 100755 index 7d335ef..01f0310 --- a/PHP/51_catch_exceptions/51_catch_exceptions.json +++ b/PHP/51_catch_exceptions/51_catch_exceptions.json @@ -1,14 +1,14 @@ -{ - "name": "Catch Exceptions", - "description": "", - "family": "code_pattern_php", - "tags": [ - "sast", - "php", - "php_v7.4.9" - ], - "instances": [ - "./1_instance_51_catch_exceptions/1_instance_51_catch_exceptions.json" - ], - "version": "v0.draft" +{ + "name": "Catch Exceptions", + "description": "./docs/description.md", + "family": "code_pattern_php", + "tags": [ + "sast", + "php", + "php_v7.4.9" + ], + "instances": [ + "./1_instance_51_catch_exceptions/1_instance_51_catch_exceptions.json" + ], + "version": "v1.0" } \ No newline at end of file diff --git a/PHP/51_catch_exceptions/Pattern Catch Exceptions.md b/PHP/51_catch_exceptions/Pattern Catch Exceptions.md deleted file mode 100644 index 3fb78c1..0000000 --- a/PHP/51_catch_exceptions/Pattern Catch Exceptions.md +++ /dev/null @@ -1,123 +0,0 @@ -# Pattern: Catch Exceptions - -## Category - -Exceptions - -## Definition - -## Instances - -### Instance 1 - -- CATEGORY: S0 -- FEATURE vs INTERNAL API: FEATURE -- INPUT SANITIZERS: NO -- SOURCES AND SINKS: NO -- NEGATIVE TEST CASES: NO -- CODE: - -```php -getMessage(), "\n"; -} -``` - -- MEASUREMENT: - -| Tool | RIPS | phpSAFE | WAP | Progpilot | Comm_1 | Comm_2 | Correct | -| ------------- | ---- | ------- | ---- | --------- | ------- | --------- | ------- | -| Vulnerability | YES | YES | NO | NO | NO | YES | YES | -Measurements Date: 8 June 2021 - -- OPCODE: - -```bash -$_main: ; (lines=28, args=0, vars=2, tmps=6) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/41_catch_exceptions/41_catch_exception.php:1-17 -L0 (9): EXT_STMT -L1 (9): T2 = FETCH_R (global) string("_GET") -L2 (9): T3 = FETCH_DIM_R T2 string("p1") -L3 (9): ASSIGN CV0($b) T3 -L4 (10): NOP -L5 (11): EXT_STMT -L6 (11): INIT_FCALL 2 176 string("inverse") -L7 (11): SEND_VAL int(5) 1 -L8 (11): SEND_VAR CV0($b) 2 -L9 (11): DO_FCALL -L10 (12): EXT_STMT -L11 (12): INIT_FCALL 2 176 string("inverse") -L12 (12): SEND_VAL int(0) 1 -L13 (12): SEND_VAR CV0($b) 2 -L14 (12): DO_FCALL -L15 (12): JMP L27 -L16 (13): CV1($e) = CATCH string("Exception") -L17 (15): EXT_STMT -L18 (15): ECHO CV0($b) -L19 (16): EXT_STMT -L20 (16): ECHO string("Caught exception: ") -L21 (16): EXT_STMT -L22 (16): INIT_METHOD_CALL 0 CV1($e) string("getMessage") -L23 (16): V7 = DO_FCALL -L24 (16): ECHO V7 -L25 (16): EXT_STMT -L26 (16): ECHO string(" -") -L27 (17): RETURN int(1) -EXCEPTION TABLE: - L5, L16, -, - - -inverse: ; (lines=16, args=2, vars=2, tmps=4) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/41_catch_exceptions/41_catch_exception.php:2-7 -L0 (2): EXT_NOP -L1 (2): CV0($x) = RECV 1 -L2 (2): CV1($b) = RECV 2 -L3 (3): EXT_STMT -L4 (3): T2 = BOOL_NOT CV0($x) -L5 (3): JMPZ T2 L11 -L6 (4): EXT_STMT -L7 (4): V3 = NEW 1 string("Exception") -L8 (4): SEND_VAR_EX CV1($b) 1 -L9 (4): DO_FCALL -L10 (4): THROW V3 -L11 (6): EXT_STMT -L12 (6): T5 = DIV int(1) CV0($x) -L13 (6): RETURN T5 -L14 (7): EXT_STMT -L15 (7): RETURN null -LIVE RANGES: - 3: L8 - L10 (new) -``` - -- DISCOVERY: - -```bash -cpg.call(".*CATCH.*").location.l -``` - -- PRECONDITIONS: - 1. - -- TRANSFORMATION: - -``` - -``` - - diff --git a/PHP/51_catch_exceptions/README.md b/PHP/51_catch_exceptions/README.md new file mode 100755 index 0000000..9600f31 --- /dev/null +++ b/PHP/51_catch_exceptions/README.md @@ -0,0 +1,144 @@ +[//]: # (This file is automatically generated. If you wish to make any changes, please use the JSON files and regenerate this file using the tpframework.) + +# Catch Exceptions + +Tags: sast, php, php_v7.4.9 + +Version: v1.0 + +## Description + +In PHP [Exceptions](https://www.php.net/manual/en/language.exceptions.php) are similar to other languages. An exception can be thrown and caught, try blocks indicate positions in code, where exceptions can occur. This pattern should target catching an exception. + +## Overview + +| Instances | has discovery rule | discovery method | rule successfull | +|---------------------------|----------------------|--------------------|--------------------| +| [1 Instance](#1-instance) | yes | joern | yes | + +## 1 Instance + +This instance focuses on the catch part of the exception. + +### Code + +```PHP +getMessage(), "\n"; +} +``` + +### Instance Properties + +| category | feature_vs_internal_api | input_sanitizer | negative_test_case | source_and_sink | +|------------|---------------------------|-------------------|----------------------|-------------------| +| S0 | FEATURE | no | no | no | + +
+ +More + +
+ + +### Compile + + +```bash +$_main: + ; (lines=20, args=0, vars=2, tmps=6) + ; (before optimizer) + ; /.../PHP/51_catch_exceptions/1_instance_51_catch_exceptions/1_instance_51_catch_exceptions.php:1-18 + ; return [] RANGE[0..0] +0000 T2 = FETCH_R (global) string("_GET") +0001 T3 = FETCH_DIM_R T2 string("p1") +0002 ASSIGN CV0($b) T3 +0003 INIT_FCALL 2 176 string("inverse") +0004 SEND_VAL int(5) 1 +0005 SEND_VAR CV0($b) 2 +0006 DO_UCALL +0007 INIT_FCALL 2 176 string("inverse") +0008 SEND_VAL int(0) 1 +0009 SEND_VAR CV0($b) 2 +0010 DO_UCALL +0011 JMP 0019 +0012 CV1($e) = CATCH string("Exception") +0013 ECHO CV0($b) +0014 ECHO string("Caught exception: ") +0015 INIT_METHOD_CALL 0 CV1($e) string("getMessage") +0016 V7 = DO_FCALL +0017 ECHO V7 +0018 ECHO string(" +") +0019 RETURN int(1) +EXCEPTION TABLE: + 0003, 0012, -, - + +inverse: + ; (lines=11, args=2, vars=2, tmps=4) + ; (before optimizer) + ; /.../PHP/51_catch_exceptions/1_instance_51_catch_exceptions/1_instance_51_catch_exceptions.php:2-7 + ; return [] RANGE[0..0] +0000 CV0($x) = RECV 1 +0001 CV1($b) = RECV 2 +0002 T2 = BOOL_NOT CV0($x) +0003 JMPZ T2 0008 +0004 V3 = NEW 1 string("Exception") +0005 SEND_VAR_EX CV1($b) 1 +0006 DO_FCALL +0007 THROW V3 +0008 T5 = DIV int(1) CV0($x) +0009 RETURN T5 +0010 RETURN null +LIVE RANGES: + 3: 0005 - 0007 (new) +``` + +
+ +
+ + +### Discovery + + +The rule searches for all calls to `CATCH` on opcode level. + +```scala +val x51 = (name, "51_catch_exception_iall", cpg.call(".*CATCH.*").location.toJson); +``` + +| discovery method | expected accuracy | +|--------------------|---------------------| +| joern | Perfect | + +
+ +
+ + +### Measurement + + +| Tool | Comm_1 | Comm_2 | phpSAFE | Progpilot | RIPS | WAP | Ground Truth | +|-------------|----------|----------|-----------|-------------|--------|-------|----------------| +| 08 Jun 2021 | no | yes | yes | no | yes | no | yes | +| 22 May 2023 | no | no | | | | | yes | + +
+ +
diff --git a/PHP/51_catch_exceptions/docs/description.md b/PHP/51_catch_exceptions/docs/description.md new file mode 100755 index 0000000..778c904 --- /dev/null +++ b/PHP/51_catch_exceptions/docs/description.md @@ -0,0 +1 @@ +In PHP [Exceptions](https://www.php.net/manual/en/language.exceptions.php) are similar to other languages. An exception can be thrown and caught, try blocks indicate positions in code, where exceptions can occur. This pattern should target catching an exception. \ No newline at end of file diff --git a/PHP/52_try_catch_finally/1_instance_52_try_catch_finally/1_instance_52_try_catch_finally.bash b/PHP/52_try_catch_finally/1_instance_52_try_catch_finally/1_instance_52_try_catch_finally.bash old mode 100644 new mode 100755 index d327481..decf16a --- a/PHP/52_try_catch_finally/1_instance_52_try_catch_finally/1_instance_52_try_catch_finally.bash +++ b/PHP/52_try_catch_finally/1_instance_52_try_catch_finally/1_instance_52_try_catch_finally.bash @@ -1,59 +1,51 @@ - -$_main: ; (lines=11, args=0, vars=1, tmps=5) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/42_try_catch_finally/first_ex/first_ex.php:1-22 -L0 (17): EXT_STMT -L1 (17): T1 = FETCH_R (global) string("_GET") -L2 (17): T2 = FETCH_DIM_R T1 string("p1") -L3 (17): ASSIGN CV0($b) T2 -L4 (18): EXT_STMT -L5 (18): INIT_FCALL 1 240 string("foo") -L6 (18): T4 = CONCAT CV0($b) string(" -") -L7 (18): SEND_VAL T4 1 -L8 (18): V5 = DO_FCALL -L9 (18): ECHO V5 -L10 (22): RETURN int(1) - -foo: ; (lines=30, args=1, vars=3, tmps=7) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/42_try_catch_finally/first_ex/first_ex.php:2-16 -L0 (2): EXT_NOP -L1 (2): CV0($b) = RECV 1 -L2 (4): EXT_STMT -L3 (4): ASSIGN CV1($bar) CV0($b) -L4 (5): NOP -L5 (6): EXT_STMT -L6 (6): V5 = NEW 1 string("Exception") -L7 (6): SEND_VAL_EX string("Exception") 1 -L8 (6): DO_FCALL -L9 (6): THROW V5 -L10 (6): JMP L18 -L11 (7): CV2($e) = CATCH string("Exception") -L12 (8): EXT_STMT -L13 (8): ECHO string("catch -") -L14 (10): EXT_STMT -L15 (10): T7 = QM_ASSIGN CV1($bar) -L16 (10): T4 = FAST_CALL L20 T7 -L17 (10): RETURN T7 -L18 (11): T4 = FAST_CALL L20 -L19 (11): JMP L28 -L20 (12): EXT_STMT -L21 (12): ECHO string("finally -") -L22 (14): EXT_STMT -L23 (14): INIT_FCALL 1 96 string("htmlspecialchars") -L24 (14): SEND_VAR CV1($bar) 1 -L25 (14): V8 = DO_FCALL -L26 (14): ASSIGN CV1($bar) V8 -L27 (14): FAST_RET T4 -L28 (16): EXT_STMT -L29 (16): RETURN null -LIVE RANGES: - 5: L7 - L9 (new) - 7: L16 - L17 (tmp/var) -EXCEPTION TABLE: - L5, L11, L20, L27 - - + +$_main: + ; (lines=10, args=0, vars=2, tmps=6) + ; (before optimizer) + ; /.../PHP/52_try_catch_finally/1_instance_52_try_catch_finally/1_instance_52_try_catch_finally.php:1-22 + ; return [] RANGE[0..0] +0000 T2 = FETCH_R (global) string("_GET") +0001 T3 = FETCH_DIM_R T2 string("p1") +0002 ASSIGN CV0($b) T3 +0003 INIT_FCALL 1 240 string("foo") +0004 T5 = CONCAT CV0($b) string(" +") +0005 SEND_VAL T5 1 +0006 V6 = DO_UCALL +0007 ASSIGN CV1($a) V6 +0008 ECHO CV1($a) +0009 RETURN int(1) + +foo: + ; (lines=21, args=1, vars=3, tmps=7) + ; (before optimizer) + ; /.../PHP/52_try_catch_finally/1_instance_52_try_catch_finally/1_instance_52_try_catch_finally.php:2-15 + ; return [] RANGE[0..0] +0000 CV0($b) = RECV 1 +0001 ASSIGN CV1($bar) CV0($b) +0002 V5 = NEW 1 string("Exception") +0003 SEND_VAL_EX string("Exception") 1 +0004 DO_FCALL +0005 THROW V5 +0006 JMP 0012 +0007 CV2($e) = CATCH string("Exception") +0008 ECHO string("catch +") +0009 T7 = QM_ASSIGN CV1($bar) +0010 T4 = FAST_CALL 0014 T7 +0011 RETURN T7 +0012 T4 = FAST_CALL 0014 +0013 JMP 0020 +0014 ECHO string("finally +") +0015 INIT_FCALL 1 96 string("htmlspecialchars") +0016 SEND_VAR CV1($bar) 1 +0017 V8 = DO_ICALL +0018 ASSIGN CV1($bar) V8 +0019 FAST_RET T4 +0020 RETURN null +LIVE RANGES: + 5: 0003 - 0005 (new) + 7: 0010 - 0011 (tmp/var) +EXCEPTION TABLE: + 0002, 0007, 0014, 0019 \ No newline at end of file diff --git a/PHP/52_try_catch_finally/1_instance_52_try_catch_finally/1_instance_52_try_catch_finally.json b/PHP/52_try_catch_finally/1_instance_52_try_catch_finally/1_instance_52_try_catch_finally.json old mode 100644 new mode 100755 index 38408cf..1797427 --- a/PHP/52_try_catch_finally/1_instance_52_try_catch_finally/1_instance_52_try_catch_finally.json +++ b/PHP/52_try_catch_finally/1_instance_52_try_catch_finally/1_instance_52_try_catch_finally.json @@ -1,37 +1,38 @@ -{ - "code": { - "path": "./1_instance_52_try_catch_finally.php", - "injection_skeleton_broken": false - }, - "discovery": { - "rule": "./1_instance_52_try_catch_finally.sc", - "method": "joern", - "rule_accuracy": null, - "notes": null - }, - "compile": { - "binary": "./1_instance_52_try_catch_finally.bash", - "instruction": null, - "dependencies": null - }, - "expectation": { - "type": "xss", - "sink_file": "./1_instance_52_try_catch_finally.php", - "sink_line": 18, - "source_file": "./1_instance_52_try_catch_finally.php", - "source_line": 17, - "expectation": true - }, - "properties": { - "category": "D2", - "feature_vs_internal_api": "FEATURE", - "input_sanitizer": false, - "source_and_sink": false, - "negative_test_case": false - }, - "remediation": { - "notes": "", - "transformation": null, - "modeling_rule": null - } +{ + "description": "This instance shows a 'try, catch, finally' construct. The unsanitized `$bar` from the `catch` will be returned.", + "code": { + "path": "./1_instance_52_try_catch_finally.php", + "injection_skeleton_broken": false + }, + "discovery": { + "rule": "../52_try_catch_finally.sc", + "method": "joern", + "rule_accuracy": "Perfect", + "notes": "The rule searches for `FAST_RET` on opcode level." + }, + "compile": { + "binary": "./1_instance_52_try_catch_finally.bash", + "instruction": null, + "dependencies": null + }, + "expectation": { + "type": "xss", + "sink_file": "./1_instance_52_try_catch_finally.php", + "sink_line": 18, + "source_file": "./1_instance_52_try_catch_finally.php", + "source_line": 16, + "expectation": true + }, + "properties": { + "category": "D2", + "feature_vs_internal_api": "FEATURE", + "input_sanitizer": false, + "source_and_sink": false, + "negative_test_case": false + }, + "remediation": { + "notes": "", + "transformation": null, + "modeling_rule": null + } } \ No newline at end of file diff --git a/PHP/52_try_catch_finally/1_instance_52_try_catch_finally/1_instance_52_try_catch_finally.php b/PHP/52_try_catch_finally/1_instance_52_try_catch_finally/1_instance_52_try_catch_finally.php old mode 100644 new mode 100755 index 31741d4..163af30 --- a/PHP/52_try_catch_finally/1_instance_52_try_catch_finally/1_instance_52_try_catch_finally.php +++ b/PHP/52_try_catch_finally/1_instance_52_try_catch_finally/1_instance_52_try_catch_finally.php @@ -1,22 +1,22 @@ - + + +## 1 Instance + + +This instance shows a 'try, catch, finally' construct. The unsanitized `$bar` from the `catch` will be returned. + +### Code + +```PHP + + +More + +
+ + +### Compile + + +```bash +$_main: + ; (lines=10, args=0, vars=2, tmps=6) + ; (before optimizer) + ; /.../PHP/52_try_catch_finally/1_instance_52_try_catch_finally/1_instance_52_try_catch_finally.php:1-22 + ; return [] RANGE[0..0] +0000 T2 = FETCH_R (global) string("_GET") +0001 T3 = FETCH_DIM_R T2 string("p1") +0002 ASSIGN CV0($b) T3 +0003 INIT_FCALL 1 240 string("foo") +0004 T5 = CONCAT CV0($b) string(" +") +0005 SEND_VAL T5 1 +0006 V6 = DO_UCALL +0007 ASSIGN CV1($a) V6 +0008 ECHO CV1($a) +0009 RETURN int(1) + +foo: + ; (lines=21, args=1, vars=3, tmps=7) + ; (before optimizer) + ; /.../PHP/52_try_catch_finally/1_instance_52_try_catch_finally/1_instance_52_try_catch_finally.php:2-15 + ; return [] RANGE[0..0] +0000 CV0($b) = RECV 1 +0001 ASSIGN CV1($bar) CV0($b) +0002 V5 = NEW 1 string("Exception") +0003 SEND_VAL_EX string("Exception") 1 +0004 DO_FCALL +0005 THROW V5 +0006 JMP 0012 +0007 CV2($e) = CATCH string("Exception") +0008 ECHO string("catch +") +0009 T7 = QM_ASSIGN CV1($bar) +0010 T4 = FAST_CALL 0014 T7 +0011 RETURN T7 +0012 T4 = FAST_CALL 0014 +0013 JMP 0020 +0014 ECHO string("finally +") +0015 INIT_FCALL 1 96 string("htmlspecialchars") +0016 SEND_VAR CV1($bar) 1 +0017 V8 = DO_ICALL +0018 ASSIGN CV1($bar) V8 +0019 FAST_RET T4 +0020 RETURN null +LIVE RANGES: + 5: 0003 - 0005 (new) + 7: 0010 - 0011 (tmp/var) +EXCEPTION TABLE: + 0002, 0007, 0014, 0019 +``` + +
+ +
+ + +### Discovery + + +The rule searches for `FAST_RET` on opcode level. + +```scala +val x52 = (name, "52_try_catch_finally_iall", cpg.call(".*FAST_RET.*").location.toJson); +``` + +| discovery method | expected accuracy | +|--------------------|---------------------| +| joern | Perfect | + +
+ +
+ + +### Measurement + + +| Tool | Comm_1 | Comm_2 | phpSAFE | Progpilot | RIPS | WAP | Ground Truth | +|-------------|----------|----------|-----------|-------------|--------|-------|----------------| +| 08 Jun 2021 | yes | yes | no | no | yes | no | yes | +| 22 May 2023 | yes | yes | | | | | yes | + +
+ + + + + +
+ + +## 2 Instance + + +This instance shows the same construct as the previous instance. But this time, the sanitized value from the finally statement is returned. + +### Code + +```PHP + + +More + +
+ + +### Compile + + +```bash +$_main: + ; (lines=10, args=0, vars=2, tmps=6) + ; (before optimizer) + ; /.../PHP/52_try_catch_finally/2_instance_52_try_catch_finally/2_instance_52_try_catch_finally.php:1-24 + ; return [] RANGE[0..0] +0000 T2 = FETCH_R (global) string("_GET") +0001 T3 = FETCH_DIM_R T2 string("p1") +0002 ASSIGN CV0($b) T3 +0003 INIT_FCALL 1 240 string("foo") +0004 T5 = CONCAT CV0($b) string(" +") +0005 SEND_VAL T5 1 +0006 V6 = DO_UCALL +0007 ASSIGN CV1($a) V6 +0008 ECHO CV1($a) +0009 RETURN int(1) + +foo: + ; (lines=23, args=1, vars=3, tmps=7) + ; (before optimizer) + ; /.../PHP/52_try_catch_finally/2_instance_52_try_catch_finally/2_instance_52_try_catch_finally.php:2-17 + ; return [] RANGE[0..0] +0000 CV0($b) = RECV 1 +0001 ASSIGN CV1($bar) CV0($b) +0002 V5 = NEW 1 string("Exception") +0003 SEND_VAL_EX string("Exception") 1 +0004 DO_FCALL +0005 THROW V5 +0006 JMP 0012 +0007 CV2($e) = CATCH string("Exception") +0008 ECHO string("catch +") +0009 T7 = QM_ASSIGN CV1($bar) +0010 T4 = FAST_CALL 0014 T7 +0011 RETURN T7 +0012 T4 = FAST_CALL 0014 +0013 JMP 0022 +0014 ECHO string("finally +") +0015 INIT_FCALL 1 96 string("htmlspecialchars") +0016 SEND_VAR CV1($bar) 1 +0017 V8 = DO_ICALL +0018 ASSIGN CV1($bar) V8 +0019 DISCARD_EXCEPTION T4 +0020 RETURN CV1($bar) +0021 FAST_RET T4 +0022 RETURN null +LIVE RANGES: + 5: 0003 - 0005 (new) + 7: 0010 - 0011 (tmp/var) +EXCEPTION TABLE: + 0002, 0007, 0014, 0021 +``` + +
+ +
+ + +### Discovery + + +The rule searches for `FAST_RET` on opcode level. + +```scala +val x52 = (name, "52_try_catch_finally_iall", cpg.call(".*FAST_RET.*").location.toJson); +``` + +| discovery method | expected accuracy | +|--------------------|---------------------| +| joern | Perfect | + +
+ +
+ + +### Measurement + + +| Tool | Comm_1 | Comm_2 | RIPS | Ground Truth | +|-------------|----------|----------|--------|----------------| +| 08 Jun 2021 | yes | yes | no | no | +| 22 May 2023 | yes | yes | | no | + +
+ +
+ + diff --git a/PHP/52_try_catch_finally/docs/description.md b/PHP/52_try_catch_finally/docs/description.md new file mode 100755 index 0000000..28bb233 --- /dev/null +++ b/PHP/52_try_catch_finally/docs/description.md @@ -0,0 +1 @@ +In PHP [Exceptions](https://www.php.net/manual/en/language.exceptions.php) are similar to other languages. An exception can be thrown and caught, try blocks indicate positions in code, where exceptions can occur. This pattern should target the try-catch-finally block. The `finally` block will always be executed, regardless of whether an exception has been thrown, and before normal execution resumes. \ No newline at end of file diff --git a/PHP/53_track_error/1_instance_53_track_error/1_instance_53_track_error.bash b/PHP/53_track_error/1_instance_53_track_error/1_instance_53_track_error.bash old mode 100644 new mode 100755 index 48041d3..2dc89f5 --- a/PHP/53_track_error/1_instance_53_track_error/1_instance_53_track_error.bash +++ b/PHP/53_track_error/1_instance_53_track_error/1_instance_53_track_error.bash @@ -1,24 +1,22 @@ - -$_main: ; (lines=18, args=0, vars=2, tmps=8) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/106_track_error/106_track_error.php:1-5 -L0 (2): EXT_STMT -L1 (2): T2 = FETCH_R (global) string("_GET") -L2 (2): T3 = FETCH_DIM_R T2 string("p1") -L3 (2): ASSIGN CV0($a) T3 -L4 (3): EXT_STMT -L5 (3): T5 = BEGIN_SILENCE -L6 (3): INIT_FCALL 1 96 string("trigger_error") -L7 (3): SEND_VAR CV0($a) 1 -L8 (3): DO_FCALL -L9 (3): END_SILENCE T5 -L10 (4): EXT_STMT -L11 (4): INIT_FCALL 0 80 string("error_get_last") -L12 (4): V7 = DO_FCALL -L13 (4): ASSIGN CV1($e) V7 -L14 (5): EXT_STMT -L15 (5): T9 = FETCH_DIM_R CV1($e) string("message") -L16 (5): ECHO T9 -L17 (5): RETURN int(1) -LIVE RANGES: - 5: L6 - L9 (silence) \ No newline at end of file + +$_main: + ; (lines=14, args=0, vars=2, tmps=8) + ; (before optimizer) + ; /.../PHP/53_track_error/1_instance_53_track_error/1_instance_53_track_error.php:1-5 + ; return [] RANGE[0..0] +0000 T2 = FETCH_R (global) string("_GET") +0001 T3 = FETCH_DIM_R T2 string("p1") +0002 ASSIGN CV0($a) T3 +0003 T5 = BEGIN_SILENCE +0004 INIT_FCALL 1 96 string("trigger_error") +0005 SEND_VAR CV0($a) 1 +0006 DO_ICALL +0007 END_SILENCE T5 +0008 INIT_FCALL 0 80 string("error_get_last") +0009 V7 = DO_ICALL +0010 ASSIGN CV1($e) V7 +0011 T9 = FETCH_DIM_R CV1($e) string("message") +0012 ECHO T9 +0013 RETURN int(1) +LIVE RANGES: + 5: 0004 - 0007 (silence) diff --git a/PHP/53_track_error/1_instance_53_track_error/1_instance_53_track_error.json b/PHP/53_track_error/1_instance_53_track_error/1_instance_53_track_error.json old mode 100644 new mode 100755 index ccab388..5f1c18e --- a/PHP/53_track_error/1_instance_53_track_error/1_instance_53_track_error.json +++ b/PHP/53_track_error/1_instance_53_track_error/1_instance_53_track_error.json @@ -1,37 +1,38 @@ -{ - "code": { - "path": "./1_instance_53_track_error.php", - "injection_skeleton_broken": false - }, - "discovery": { - "rule": "./1_instance_53_track_error.sc", - "method": "joern", - "rule_accuracy": null, - "notes": null - }, - "compile": { - "binary": "./1_instance_53_track_error.bash", - "instruction": null, - "dependencies": null - }, - "expectation": { - "type": "xss", - "sink_file": "./1_instance_53_track_error.php", - "sink_line": 5, - "source_file": "./1_instance_53_track_error.php", - "source_line": 2, - "expectation": true - }, - "properties": { - "category": "S0", - "feature_vs_internal_api": "FEATURE", - "input_sanitizer": false, - "source_and_sink": false, - "negative_test_case": false - }, - "remediation": { - "notes": "", - "transformation": null, - "modeling_rule": null - } +{ + "description": "The instance triggers an error, which will is suppressed using the @ operator. Afterwards the error will be retrieved and the user controlled value outputted.", + "code": { + "path": "./1_instance_53_track_error.php", + "injection_skeleton_broken": false + }, + "discovery": { + "rule": "./1_instance_53_track_error.sc", + "method": "joern", + "rule_accuracy": "Perfect", + "notes": "The discovery rule searches for `BEGIN_SILENCE` on opcode level." + }, + "compile": { + "binary": "./1_instance_53_track_error.bash", + "instruction": null, + "dependencies": null + }, + "expectation": { + "type": "xss", + "sink_file": "./1_instance_53_track_error.php", + "sink_line": 5, + "source_file": "./1_instance_53_track_error.php", + "source_line": 2, + "expectation": true + }, + "properties": { + "category": "S0", + "feature_vs_internal_api": "FEATURE", + "input_sanitizer": false, + "source_and_sink": false, + "negative_test_case": false + }, + "remediation": { + "notes": "", + "transformation": null, + "modeling_rule": null + } } \ No newline at end of file diff --git a/PHP/53_track_error/1_instance_53_track_error/1_instance_53_track_error.php b/PHP/53_track_error/1_instance_53_track_error/1_instance_53_track_error.php old mode 100644 new mode 100755 index 4fec4f1..f004574 --- a/PHP/53_track_error/1_instance_53_track_error/1_instance_53_track_error.php +++ b/PHP/53_track_error/1_instance_53_track_error/1_instance_53_track_error.php @@ -1,5 +1,5 @@ - + +More + +
+ + +### Compile + + +```bash +$_main: + ; (lines=14, args=0, vars=2, tmps=8) + ; (before optimizer) + ; /.../PHP/53_track_error/1_instance_53_track_error/1_instance_53_track_error.php:1-5 + ; return [] RANGE[0..0] +0000 T2 = FETCH_R (global) string("_GET") +0001 T3 = FETCH_DIM_R T2 string("p1") +0002 ASSIGN CV0($a) T3 +0003 T5 = BEGIN_SILENCE +0004 INIT_FCALL 1 96 string("trigger_error") +0005 SEND_VAR CV0($a) 1 +0006 DO_ICALL +0007 END_SILENCE T5 +0008 INIT_FCALL 0 80 string("error_get_last") +0009 V7 = DO_ICALL +0010 ASSIGN CV1($e) V7 +0011 T9 = FETCH_DIM_R CV1($e) string("message") +0012 ECHO T9 +0013 RETURN int(1) +LIVE RANGES: + 5: 0004 - 0007 (silence) +``` + +
+ +
+ + +### Discovery + + +The discovery rule searches for `BEGIN_SILENCE` on opcode level. + +```scala +val x53 = (name, "53_track_error_iall", cpg.call(".*BEGIN_SILENCE.*").location.toJson); +``` + +| discovery method | expected accuracy | +|--------------------|---------------------| +| joern | Perfect | + +
+ +
+ + +### Measurement + + +| Tool | Comm_1 | Comm_2 | phpSAFE | Progpilot | RIPS | WAP | Ground Truth | +|-------------|----------|----------|-----------|-------------|--------|-------|----------------| +| 08 Jun 2021 | no | no | no | yes | no | no | yes | +| 22 May 2023 | no | no | | | | | yes | + +
+ + diff --git a/PHP/53_track_error/docs/description.md b/PHP/53_track_error/docs/description.md new file mode 100755 index 0000000..f688b1a --- /dev/null +++ b/PHP/53_track_error/docs/description.md @@ -0,0 +1 @@ +PHP suppports one [error control operator](https://www.php.net/manual/en/language.operators.errorcontrol.php). When the @ symbol is prepended to an expression in PHP, any error that might occur when evaluating this expression will be suppressed. \ No newline at end of file diff --git a/PHP/54_generators/1_instance_54_generators/1_instance_54_generators.bash b/PHP/54_generators/1_instance_54_generators/1_instance_54_generators.bash old mode 100644 new mode 100755 index 4ca6869..2198cf6 --- a/PHP/54_generators/1_instance_54_generators/1_instance_54_generators.bash +++ b/PHP/54_generators/1_instance_54_generators/1_instance_54_generators.bash @@ -1,47 +1,36 @@ - -$_main: ; (lines=19, args=0, vars=3, tmps=7) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/50_generators/first_ex/first_ex.php:1-12 -L0 (7): EXT_STMT -L1 (7): T3 = FETCH_R (global) string("_GET") -L2 (7): T4 = FETCH_DIM_R T3 string("p1") -L3 (7): ASSIGN CV0($b) T4 -L4 (8): EXT_STMT -L5 (8): INIT_FCALL 1 176 string("gen_one_to_three") -L6 (8): SEND_VAR CV0($b) 1 -L7 (8): V6 = DO_FCALL -L8 (8): ASSIGN CV1($generator) V6 -L9 (9): EXT_STMT -L10 (9): V8 = FE_RESET_R CV1($generator) L17 -L11 (9): FE_FETCH_R V8 CV2($value) L17 -L12 (11): EXT_STMT -L13 (11): NOP -L14 (11): T9 = FAST_CONCAT CV2($value) string(" -") -L15 (11): ECHO T9 -L16 (9): JMP L11 -L17 (9): FE_FREE V8 -L18 (12): RETURN int(1) -LIVE RANGES: - 8: L11 - L17 (loop) - -gen_one_to_three: ; (lines=15, args=1, vars=2, tmps=4) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/50_generators/first_ex/first_ex.php:2-6 -L0 (2): EXT_NOP -L1 (2): CV0($b) = RECV 1 -L2 (2): GENERATOR_CREATE -L3 (3): EXT_STMT -L4 (3): ASSIGN CV1($i) int(1) -L5 (3): JMP L10 -L6 (4): EXT_STMT -L7 (4): YIELD CV0($b) -L8 (3): T4 = POST_INC CV1($i) -L9 (3): FREE T4 -L10 (3): T5 = IS_SMALLER_OR_EQUAL CV1($i) int(3) -L11 (3): EXT_STMT -L12 (3): JMPNZ T5 L6 -L13 (6): EXT_STMT -L14 (6): GENERATOR_RETURN null -LIVE RANGES: - 5: L11 - L12 (tmp/var) + +$_main: + ; (lines=13, args=0, vars=3, tmps=6) + ; (before optimizer) + ; /.../PHP/54_generators/1_instance_54_generators/1_instance_54_generators.php:1-13 + ; return [] RANGE[0..0] +0000 T3 = FETCH_R (global) string("_GET") +0001 T4 = FETCH_DIM_R T3 string("p1") +0002 ASSIGN CV0($b) T4 +0003 INIT_FCALL 1 176 string("gen_one_to_three") +0004 SEND_VAR CV0($b) 1 +0005 V6 = DO_UCALL +0006 ASSIGN CV1($generator) V6 +0007 V8 = FE_RESET_R CV1($generator) 0011 +0008 FE_FETCH_R V8 CV2($value) 0011 +0009 ECHO CV2($value) +0010 JMP 0008 +0011 FE_FREE V8 +0012 RETURN int(1) +LIVE RANGES: + 8: 0008 - 0011 (loop) + +gen_one_to_three: + ; (lines=9, args=1, vars=2, tmps=4) + ; (before optimizer) + ; /.../PHP/54_generators/1_instance_54_generators/1_instance_54_generators.php:2-6 + ; return [] RANGE[0..0] +0000 CV0($b) = RECV 1 +0001 GENERATOR_CREATE +0002 ASSIGN CV1($i) int(1) +0003 JMP 0006 +0004 YIELD CV0($b) +0005 PRE_INC CV1($i) +0006 T5 = IS_SMALLER_OR_EQUAL CV1($i) int(3) +0007 JMPNZ T5 0004 +0008 GENERATOR_RETURN null diff --git a/PHP/54_generators/1_instance_54_generators/1_instance_54_generators.json b/PHP/54_generators/1_instance_54_generators/1_instance_54_generators.json old mode 100644 new mode 100755 index 65886f7..fab8f38 --- a/PHP/54_generators/1_instance_54_generators/1_instance_54_generators.json +++ b/PHP/54_generators/1_instance_54_generators/1_instance_54_generators.json @@ -1,37 +1,38 @@ -{ - "code": { - "path": "./1_instance_54_generators.php", - "injection_skeleton_broken": false - }, - "discovery": { - "rule": "./1_instance_54_generators.sc", - "method": "joern", - "rule_accuracy": null, - "notes": null - }, - "compile": { - "binary": "./1_instance_54_generators.bash", - "instruction": null, - "dependencies": null - }, - "expectation": { - "type": "xss", - "sink_file": "./1_instance_54_generators.php", - "sink_line": 11, - "source_file": "./1_instance_54_generators.php", - "source_line": 7, - "expectation": true - }, - "properties": { - "category": "S0", - "feature_vs_internal_api": "FEATURE", - "input_sanitizer": false, - "source_and_sink": false, - "negative_test_case": false - }, - "remediation": { - "notes": "", - "transformation": null, - "modeling_rule": null - } +{ + "description": "The instance shows a simple generator, that generates the input value three times.", + "code": { + "path": "./1_instance_54_generators.php", + "injection_skeleton_broken": false + }, + "discovery": { + "rule": "./1_instance_54_generators.sc", + "method": "joern", + "rule_accuracy": "Perfect", + "notes": "The discovery rule searches for `GENERATOR_CREATE` on opcode level." + }, + "compile": { + "binary": "./1_instance_54_generators.bash", + "instruction": null, + "dependencies": null + }, + "expectation": { + "type": "xss", + "sink_file": "./1_instance_54_generators.php", + "sink_line": 12, + "source_file": "./1_instance_54_generators.php", + "source_line": 8, + "expectation": true + }, + "properties": { + "category": "S0", + "feature_vs_internal_api": "FEATURE", + "input_sanitizer": false, + "source_and_sink": false, + "negative_test_case": false + }, + "remediation": { + "notes": "", + "transformation": null, + "modeling_rule": null + } } \ No newline at end of file diff --git a/PHP/54_generators/1_instance_54_generators/1_instance_54_generators.php b/PHP/54_generators/1_instance_54_generators/1_instance_54_generators.php old mode 100644 new mode 100755 index 3b59e51..202e32f --- a/PHP/54_generators/1_instance_54_generators/1_instance_54_generators.php +++ b/PHP/54_generators/1_instance_54_generators/1_instance_54_generators.php @@ -1,12 +1,13 @@ - + +More + +
+ + +### Compile + + +```bash +$_main: + ; (lines=13, args=0, vars=3, tmps=6) + ; (before optimizer) + ; /.../PHP/54_generators/1_instance_54_generators/1_instance_54_generators.php:1-13 + ; return [] RANGE[0..0] +0000 T3 = FETCH_R (global) string("_GET") +0001 T4 = FETCH_DIM_R T3 string("p1") +0002 ASSIGN CV0($b) T4 +0003 INIT_FCALL 1 176 string("gen_one_to_three") +0004 SEND_VAR CV0($b) 1 +0005 V6 = DO_UCALL +0006 ASSIGN CV1($generator) V6 +0007 V8 = FE_RESET_R CV1($generator) 0011 +0008 FE_FETCH_R V8 CV2($value) 0011 +0009 ECHO CV2($value) +0010 JMP 0008 +0011 FE_FREE V8 +0012 RETURN int(1) +LIVE RANGES: + 8: 0008 - 0011 (loop) + +gen_one_to_three: + ; (lines=9, args=1, vars=2, tmps=4) + ; (before optimizer) + ; /.../PHP/54_generators/1_instance_54_generators/1_instance_54_generators.php:2-6 + ; return [] RANGE[0..0] +0000 CV0($b) = RECV 1 +0001 GENERATOR_CREATE +0002 ASSIGN CV1($i) int(1) +0003 JMP 0006 +0004 YIELD CV0($b) +0005 PRE_INC CV1($i) +0006 T5 = IS_SMALLER_OR_EQUAL CV1($i) int(3) +0007 JMPNZ T5 0004 +0008 GENERATOR_RETURN null +``` + +
+ +
+ + +### Discovery + + +The discovery rule searches for `GENERATOR_CREATE` on opcode level. + +```scala +val x54 = (name, "54_generators_iall", cpg.call(".*GENERATOR_CREATE.*").location.toJson); +``` + +| discovery method | expected accuracy | +|--------------------|---------------------| +| joern | Perfect | + +
+ +
+ + +### Measurement + + +| Tool | Comm_1 | Comm_2 | phpSAFE | Progpilot | RIPS | WAP | Ground Truth | +|-------------|----------|----------|-----------|-------------|--------|-------|----------------| +| 08 Jun 2021 | yes | no | no | no | yes | no | yes | +| 22 May 2023 | yes | no | | | | | yes | + +
+ + diff --git a/PHP/54_generators/docs/description.md b/PHP/54_generators/docs/description.md new file mode 100755 index 0000000..6bda900 --- /dev/null +++ b/PHP/54_generators/docs/description.md @@ -0,0 +1 @@ +A [generator](https://www.php.net/manual/en/language.generators.overview.php) in PHP provides an easy way to implement simple iterators without implementing a class that implements the Iterator interface. \ No newline at end of file diff --git a/PHP/55_goto/1_instance_55_goto/1_instance_55_goto.bash b/PHP/55_goto/1_instance_55_goto/1_instance_55_goto.bash old mode 100644 new mode 100755 index 67354ff..87634e3 --- a/PHP/55_goto/1_instance_55_goto/1_instance_55_goto.bash +++ b/PHP/55_goto/1_instance_55_goto/1_instance_55_goto.bash @@ -1,22 +1,17 @@ - -$_main: ; (lines=18, args=0, vars=2, tmps=7) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/69_goto/69_goto.php:1-12 -L0 (2): EXT_STMT -L1 (2): ASSIGN CV0($b) string("abcde") -L2 (3): EXT_STMT -L3 (3): ASSIGN CV1($cond) int(1) -L4 (5): EXT_STMT -L5 (5): ECHO CV0($b) -L6 (6): EXT_STMT -L7 (6): T4 = FETCH_R (global) string("_GET") -L8 (6): T5 = FETCH_DIM_R T4 string("p1") -L9 (6): ASSIGN CV0($b) T5 -L10 (9): EXT_STMT -L11 (9): T7 = IS_EQUAL CV1($cond) int(1) -L12 (9): JMPZ T7 L17 -L13 (10): EXT_STMT -L14 (10): ASSIGN CV1($cond) int(0) -L15 (11): EXT_STMT -L16 (11): JMP L4 -L17 (12): RETURN int(1) + +$_main: + ; (lines=11, args=0, vars=2, tmps=7) + ; (before optimizer) + ; /.../PHP/55_goto/1_instance_55_goto/1_instance_55_goto.php:1-12 + ; return [] RANGE[0..0] +0000 ASSIGN CV0($b) string("abcde") +0001 ASSIGN CV1($cond) int(1) +0002 ECHO CV0($b) +0003 T4 = FETCH_R (global) string("_GET") +0004 T5 = FETCH_DIM_R T4 string("p1") +0005 ASSIGN CV0($b) T5 +0006 T7 = IS_EQUAL CV1($cond) int(1) +0007 JMPZ T7 0010 +0008 ASSIGN CV1($cond) int(0) +0009 JMP 0002 +0010 RETURN int(1) diff --git a/PHP/55_goto/1_instance_55_goto/1_instance_55_goto.json b/PHP/55_goto/1_instance_55_goto/1_instance_55_goto.json old mode 100644 new mode 100755 index d73c36f..3abd0cf --- a/PHP/55_goto/1_instance_55_goto/1_instance_55_goto.json +++ b/PHP/55_goto/1_instance_55_goto/1_instance_55_goto.json @@ -1,37 +1,38 @@ -{ - "code": { - "path": "./1_instance_55_goto.php", - "injection_skeleton_broken": false - }, - "discovery": { - "rule": "./1_instance_55_goto.py", - "method": "python", - "rule_accuracy": null, - "notes": null - }, - "compile": { - "binary": "./1_instance_55_goto.bash", - "instruction": null, - "dependencies": null - }, - "expectation": { - "type": "xss", - "sink_file": "./1_instance_55_goto.php", - "sink_line": 5, - "source_file": "./1_instance_55_goto.php", - "source_line": 6, - "expectation": true - }, - "properties": { - "category": "S0", - "feature_vs_internal_api": "FEATURE", - "input_sanitizer": false, - "source_and_sink": false, - "negative_test_case": false - }, - "remediation": { - "notes": "", - "transformation": null, - "modeling_rule": null - } +{ + "description": "This instance shows a simple `goto` programm.", + "code": { + "path": "./1_instance_55_goto.php", + "injection_skeleton_broken": false + }, + "discovery": { + "rule": "./1_instance_55_goto.py", + "method": "python", + "rule_accuracy": null, + "notes": "This instance could profit from source code discovery, as `goto` cannot be found on opcode level. The current rule is written in python and uses `grep` to find the keyword `goto`" + }, + "compile": { + "binary": "./1_instance_55_goto.bash", + "instruction": null, + "dependencies": null + }, + "expectation": { + "type": "xss", + "sink_file": "./1_instance_55_goto.php", + "sink_line": 5, + "source_file": "./1_instance_55_goto.php", + "source_line": 6, + "expectation": true + }, + "properties": { + "category": "S0", + "feature_vs_internal_api": "FEATURE", + "input_sanitizer": false, + "source_and_sink": false, + "negative_test_case": false + }, + "remediation": { + "notes": "", + "transformation": null, + "modeling_rule": null + } } \ No newline at end of file diff --git a/PHP/55_goto/1_instance_55_goto/1_instance_55_goto.php b/PHP/55_goto/1_instance_55_goto/1_instance_55_goto.php old mode 100644 new mode 100755 index 1650cc3..6df363a --- a/PHP/55_goto/1_instance_55_goto/1_instance_55_goto.php +++ b/PHP/55_goto/1_instance_55_goto/1_instance_55_goto.php @@ -1,12 +1,12 @@ - + +More + +
+ + +### Compile + + +```bash +$_main: + ; (lines=11, args=0, vars=2, tmps=7) + ; (before optimizer) + ; /.../PHP/55_goto/1_instance_55_goto/1_instance_55_goto.php:1-12 + ; return [] RANGE[0..0] +0000 ASSIGN CV0($b) string("abcde") +0001 ASSIGN CV1($cond) int(1) +0002 ECHO CV0($b) +0003 T4 = FETCH_R (global) string("_GET") +0004 T5 = FETCH_DIM_R T4 string("p1") +0005 ASSIGN CV0($b) T5 +0006 T7 = IS_EQUAL CV1($cond) int(1) +0007 JMPZ T7 0010 +0008 ASSIGN CV1($cond) int(0) +0009 JMP 0002 +0010 RETURN int(1) +``` + +
+ +
+ + +### Discovery + + +This instance could profit from source code discovery, as `goto` cannot be found on opcode level. The current rule is written in python and uses `grep` to find the keyword `goto` + +```python +#!/usr/bin/env python3 + +import subprocess +import sys +from pathlib import Path + +if __name__ == "__main__": +project_folder = Path(sys.argv[1]) +cmd = "grep -Hn -r 'goto' " + str(project_folder) +try: +result = subprocess.check_output(cmd, shell=True) +results = result.decode('utf-8-sig') +results = results.split('\n') +for r in results[:-1]: +values = r.split(':') +line = values[1] +file = values[0] +if '.php' in file: +print(file + '%' + line) +except: +#no line found +print('') +``` + +| discovery method | expected accuracy | +|--------------------|---------------------| +| python | | + +
+ +
+ + +### Measurement + + +| Tool | Comm_1 | Comm_2 | phpSAFE | Progpilot | RIPS | WAP | Ground Truth | +|-------------|----------|----------|-----------|-------------|--------|-------|----------------| +| 08 Jun 2021 | yes | no | no | no | no | no | yes | +| 22 May 2023 | yes | no | | | | | yes | + +
+ + diff --git a/PHP/55_goto/docs/description.md b/PHP/55_goto/docs/description.md new file mode 100755 index 0000000..bdae90c --- /dev/null +++ b/PHP/55_goto/docs/description.md @@ -0,0 +1 @@ +The [goto](https://www.php.net/manual/en/control-structures.goto.php) statement can be used, to jump to another section in the programm indicated by a label. This is considered bad coding style. \ No newline at end of file diff --git a/PHP/56_exit/1_instance_56_exit/1_instance_56_exit.bash b/PHP/56_exit/1_instance_56_exit/1_instance_56_exit.bash old mode 100644 new mode 100755 index 191c384..289bbbf --- a/PHP/56_exit/1_instance_56_exit/1_instance_56_exit.bash +++ b/PHP/56_exit/1_instance_56_exit/1_instance_56_exit.bash @@ -1,11 +1,11 @@ - -$_main: ; (lines=7, args=0, vars=1, tmps=3) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/108_exit/108_exit.php:1-5 -L0 (2): EXT_STMT -L1 (2): T1 = FETCH_R (global) string("_GET") -L2 (2): T2 = FETCH_DIM_R T1 string("p1") -L3 (2): ASSIGN CV0($a) T2 -L4 (3): EXT_STMT -L5 (3): EXIT CV0($a) -L6 (5): RETURN int(1) + +$_main: + ; (lines=5, args=0, vars=1, tmps=3) + ; (before optimizer) + ; /.../PHP/56_exit/1_instance_56_exit/1_instance_56_exit.php:1-3 + ; return [] RANGE[0..0] +0000 T1 = FETCH_R (global) string("_GET") +0001 T2 = FETCH_DIM_R T1 string("p1") +0002 ASSIGN CV0($a) T2 +0003 EXIT CV0($a) +0004 RETURN int(1) diff --git a/PHP/56_exit/1_instance_56_exit/1_instance_56_exit.json b/PHP/56_exit/1_instance_56_exit/1_instance_56_exit.json old mode 100644 new mode 100755 index c18f8b8..e297baf --- a/PHP/56_exit/1_instance_56_exit/1_instance_56_exit.json +++ b/PHP/56_exit/1_instance_56_exit/1_instance_56_exit.json @@ -1,37 +1,38 @@ -{ - "code": { - "path": "./1_instance_56_exit.php", - "injection_skeleton_broken": false - }, - "discovery": { - "rule": "./1_instance_56_exit.sc", - "method": "joern", - "rule_accuracy": null, - "notes": null - }, - "compile": { - "binary": "./1_instance_56_exit.bash", - "instruction": null, - "dependencies": null - }, - "expectation": { - "type": "xss", - "sink_file": "./1_instance_56_exit.php", - "sink_line": 3, - "source_file": "./1_instance_56_exit.php", - "source_line": 2, - "expectation": true - }, - "properties": { - "category": "S0", - "feature_vs_internal_api": "FEATURE", - "input_sanitizer": false, - "source_and_sink": true, - "negative_test_case": false - }, - "remediation": { - "notes": "", - "transformation": null, - "modeling_rule": null - } +{ + "description": "The instance demonstrates `exit`, where the argument for that function is a user controlled value.", + "code": { + "path": "./1_instance_56_exit.php", + "injection_skeleton_broken": false + }, + "discovery": { + "rule": "./1_instance_56_exit.sc", + "method": "joern", + "rule_accuracy": "Perfect", + "notes": "The rule searches for `EXIT` on opcode level and filters out those, where the argument is a variable." + }, + "compile": { + "binary": "./1_instance_56_exit.bash", + "instruction": null, + "dependencies": null + }, + "expectation": { + "type": "xss", + "sink_file": "./1_instance_56_exit.php", + "sink_line": 3, + "source_file": "./1_instance_56_exit.php", + "source_line": 2, + "expectation": true + }, + "properties": { + "category": "S0", + "feature_vs_internal_api": "FEATURE", + "input_sanitizer": false, + "source_and_sink": true, + "negative_test_case": false + }, + "remediation": { + "notes": "", + "transformation": null, + "modeling_rule": null + } } \ No newline at end of file diff --git a/PHP/56_exit/1_instance_56_exit/1_instance_56_exit.php b/PHP/56_exit/1_instance_56_exit/1_instance_56_exit.php old mode 100644 new mode 100755 index 46d9628..de125e6 --- a/PHP/56_exit/1_instance_56_exit/1_instance_56_exit.php +++ b/PHP/56_exit/1_instance_56_exit/1_instance_56_exit.php @@ -1,4 +1,3 @@ - + -``` - -- MEASUREMENT: - -| Tool | RIPS | phpSAFE | WAP | Progpilot | Comm_1 | Comm_2 | Correct | -| ------------- | ---- | ------- | ---- | --------- | ------- | --------- | ------- | -| Vulnerability | YES | YES | YES | YES | NO | NO | YES | - -Measurements Date: 8 June 2021 - -- OPCODE: - -```bash -$_main: ; (lines=7, args=0, vars=1, tmps=3) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/108_exit/108_exit.php:1-5 -L0 (2): EXT_STMT -L1 (2): T1 = FETCH_R (global) string("_GET") -L2 (2): T2 = FETCH_DIM_R T1 string("p1") -L3 (2): ASSIGN CV0($a) T2 -L4 (3): EXT_STMT -L5 (3): EXIT CV0($a) -L6 (5): RETURN int(1) -``` - -- DISCOVERY: - -```bash -cpg.call(".*EXIT.*").argument.code("CV.*|T.*|V.*").location.l -``` - -- PRECONDITIONS: - 1. -- TRANSFORMATION: - -``` -``` - diff --git a/PHP/56_exit/README.md b/PHP/56_exit/README.md new file mode 100755 index 0000000..94041ff --- /dev/null +++ b/PHP/56_exit/README.md @@ -0,0 +1,93 @@ +[//]: # (This file is automatically generated. If you wish to make any changes, please use the JSON files and regenerate this file using the tpframework.) + +# Exit + +Tags: sast, php, php_v7.4.9 + +Version: v1.0 + +## Description + +In PHP [`exit`](https://www.php.net/manual/en/function.exit.php) outputs a message and terminates the script. + +## Overview + +| Instances | has discovery rule | discovery method | rule successfull | +|---------------------------|----------------------|--------------------|--------------------| +| [1 Instance](#1-instance) | yes | joern | yes | + +## 1 Instance + +The instance demonstrates `exit`, where the argument for that function is a user controlled value. + +### Code + +```PHP + + +More + +
+ + +### Compile + + +```bash +$_main: + ; (lines=5, args=0, vars=1, tmps=3) + ; (before optimizer) + ; /.../PHP/56_exit/1_instance_56_exit/1_instance_56_exit.php:1-3 + ; return [] RANGE[0..0] +0000 T1 = FETCH_R (global) string("_GET") +0001 T2 = FETCH_DIM_R T1 string("p1") +0002 ASSIGN CV0($a) T2 +0003 EXIT CV0($a) +0004 RETURN int(1) +``` + +
+ +
+ + +### Discovery + + +The rule searches for `EXIT` on opcode level and filters out those, where the argument is a variable. + +```scala +val x56 = (name, "55_Exit_iall", cpg.call(".*EXIT.*").argument.code("CV.*|T.*|V.*").location.toJson); +``` + +| discovery method | expected accuracy | +|--------------------|---------------------| +| joern | Perfect | + +
+ +
+ + +### Measurement + + +| Tool | Comm_1 | Comm_2 | phpSAFE | Progpilot | RIPS | WAP | Ground Truth | +|-------------|----------|----------|-----------|-------------|--------|-------|----------------| +| 08 Jun 2021 | no | no | yes | yes | yes | yes | yes | +| 22 May 2023 | no | no | | | | | yes | + +
+ + diff --git a/PHP/56_exit/docs/description.md b/PHP/56_exit/docs/description.md new file mode 100755 index 0000000..2d353c1 --- /dev/null +++ b/PHP/56_exit/docs/description.md @@ -0,0 +1,2 @@ +In PHP [`exit`](https://www.php.net/manual/en/function.exit.php) outputs a message and terminates the script. + diff --git a/PHP/57_js_redirect/1_instance_57_js_redirect/1_instance_57_js_redirect.bash b/PHP/57_js_redirect/1_instance_57_js_redirect/1_instance_57_js_redirect.bash deleted file mode 100644 index d27e932..0000000 --- a/PHP/57_js_redirect/1_instance_57_js_redirect/1_instance_57_js_redirect.bash +++ /dev/null @@ -1,18 +0,0 @@ - -$_main: ; (lines=12, args=0, vars=0, tmps=5) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/125_window_location/125_window_location.php:1-8 -L0 (3): EXT_STMT -L1 (3): INIT_FCALL 0 80 string("session_start") -L2 (3): DO_FCALL -L3 (5): EXT_STMT -L4 (5): T3 = FETCH_R (global) string("_GET") -L5 (5): T4 = FETCH_DIM_R T3 string("p1") -L6 (5): V1 = FETCH_W (global) string("_SESSION") -L7 (5): ASSIGN_DIM V1 string("abc") -L8 (5): OP_DATA T4 -L9 (7): EXT_STMT -L10 (7): ECHO string("") -L11 (8): RETURN int(1) -LIVE RANGES: - 4: L6 - L7 (tmp/var) \ No newline at end of file diff --git a/PHP/57_js_redirect/1_instance_57_js_redirect/1_instance_57_js_redirect.json b/PHP/57_js_redirect/1_instance_57_js_redirect/1_instance_57_js_redirect.json old mode 100644 new mode 100755 index bda6932..eedde4f --- a/PHP/57_js_redirect/1_instance_57_js_redirect/1_instance_57_js_redirect.json +++ b/PHP/57_js_redirect/1_instance_57_js_redirect/1_instance_57_js_redirect.json @@ -1,37 +1,38 @@ -{ - "code": { - "path": "./1_instance_57_js_redirect.php", - "injection_skeleton_broken": false - }, - "discovery": { - "rule": "./1_instance_57_js_redirect.sc", - "method": "joern", - "rule_accuracy": null, - "notes": null - }, - "compile": { - "binary": "./1_instance_57_js_redirect.bash", - "instruction": null, - "dependencies": null - }, - "expectation": { - "type": "xss", - "sink_file": "./1_instance_57_js_redirect.php", - "sink_line": 5, - "source_file": "./1_instance_57_js_redirect.php", - "source_line": 5, - "expectation": true - }, - "properties": { - "category": "S0", - "feature_vs_internal_api": "FEATURE", - "input_sanitizer": false, - "source_and_sink": false, - "negative_test_case": false - }, - "remediation": { - "notes": "", - "transformation": null, - "modeling_rule": null - } +{ + "description": "The instance consists of two PHP files, the first file redirects to the second file, that contains the sink.", + "code": { + "path": "./1_instance_57_js_redirect_1.php", + "injection_skeleton_broken": false + }, + "discovery": { + "rule": "./1_instance_57_js_redirect.sc", + "method": "joern", + "rule_accuracy": "FP", + "notes": "The rule searches for a redirect written in JS in the code." + }, + "compile": { + "binary": "./1_instance_57_js_redirect_1.bash", + "instruction": null, + "dependencies": null + }, + "expectation": { + "type": "xss", + "sink_file": "./1_instance_57_js_redirect_0.php", + "sink_line": 4, + "source_file": "./1_instance_57_js_redirect_1.php", + "source_line": 4, + "expectation": true + }, + "properties": { + "category": "S0", + "feature_vs_internal_api": "FEATURE", + "input_sanitizer": false, + "source_and_sink": false, + "negative_test_case": false + }, + "remediation": { + "notes": "", + "transformation": null, + "modeling_rule": null + } } \ No newline at end of file diff --git a/PHP/57_js_redirect/1_instance_57_js_redirect/1_instance_57_js_redirect.php b/PHP/57_js_redirect/1_instance_57_js_redirect/1_instance_57_js_redirect.php deleted file mode 100644 index 15b8172..0000000 --- a/PHP/57_js_redirect/1_instance_57_js_redirect/1_instance_57_js_redirect.php +++ /dev/null @@ -1,7 +0,0 @@ - window.location='a.php' "; diff --git a/PHP/57_js_redirect/1_instance_57_js_redirect/1_instance_57_js_redirect.py b/PHP/57_js_redirect/1_instance_57_js_redirect/1_instance_57_js_redirect.py deleted file mode 100644 index 9be0061..0000000 --- a/PHP/57_js_redirect/1_instance_57_js_redirect/1_instance_57_js_redirect.py +++ /dev/null @@ -1,22 +0,0 @@ -#!/usr/bin/env python3 - -import subprocess -import sys -from pathlib import Path - -if __name__ == "__main__": - project_folder = Path(sys.argv[1]) - cmd = "grep -Hn -r 'window.location' " + str(project_folder) - try: - result = subprocess.check_output(cmd, shell=True) - results = result.decode('utf-8-sig') - results = results.split('\n') - for r in results[:-1]: - values = r.split(':') - line = values[1] - file = values[0] - if '.php' in file: - print(file + '%' + line) - except: - #no line found - print('') diff --git a/PHP/57_js_redirect/1_instance_57_js_redirect/1_instance_57_js_redirect.sc b/PHP/57_js_redirect/1_instance_57_js_redirect/1_instance_57_js_redirect.sc new file mode 100755 index 0000000..27d5261 --- /dev/null +++ b/PHP/57_js_redirect/1_instance_57_js_redirect/1_instance_57_js_redirect.sc @@ -0,0 +1,7 @@ +@main def main(name : String): Unit = { + importCpg(name) + // TODO: replace line below with your detection query + val x57 = (name, "57_js_redirect_iall", cpg.call.code(".*") +0008 RETURN int(1) +LIVE RANGES: + 4: 0004 - 0005 (tmp/var) diff --git a/PHP/57_js_redirect/1_instance_57_js_redirect/1_instance_57_js_redirect_1.php b/PHP/57_js_redirect/1_instance_57_js_redirect/1_instance_57_js_redirect_1.php new file mode 100755 index 0000000..12ad97c --- /dev/null +++ b/PHP/57_js_redirect/1_instance_57_js_redirect/1_instance_57_js_redirect_1.php @@ -0,0 +1,6 @@ + window.location='1_instance_57_js_redirect_0.php' "; diff --git a/PHP/57_js_redirect/1_instance_57_js_redirect/a.bash b/PHP/57_js_redirect/1_instance_57_js_redirect/a.bash deleted file mode 100644 index 72e3fa4..0000000 --- a/PHP/57_js_redirect/1_instance_57_js_redirect/a.bash +++ /dev/null @@ -1,12 +0,0 @@ - -$_main: ; (lines=8, args=0, vars=0, tmps=3) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/125_JS_redirect/a.php:1-5 -L0 (3): EXT_STMT -L1 (3): INIT_FCALL 0 80 string("session_start") -L2 (3): DO_FCALL -L3 (5): EXT_STMT -L4 (5): T1 = FETCH_R (global) string("_SESSION") -L5 (5): T2 = FETCH_DIM_R T1 string("abc") -L6 (5): ECHO T2 -L7 (5): RETURN int(1) diff --git a/PHP/57_js_redirect/1_instance_57_js_redirect/a.php b/PHP/57_js_redirect/1_instance_57_js_redirect/a.php deleted file mode 100644 index 9f6d8b2..0000000 --- a/PHP/57_js_redirect/1_instance_57_js_redirect/a.php +++ /dev/null @@ -1,5 +0,0 @@ - window.location='a.php' "; - -// a.php - -session_start(); - -echo $_SESSION["abc"]; -``` - -- MEASUREMENT: - -| Tool | RIPS | phpSAFE | WAP | Progpilot | Comm_1 | Comm_2 | Correct | -| ------------- | ---- | ------- | ---- | --------- | ------- | --------- | ------- | -| Vulnerability | NO | NO | NO | NO | NO | NO | YES | -Measurements Date: 8 June 2021 - -- OPCODE: - -```bash - -$_main: ; (lines=12, args=0, vars=0, tmps=5) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/125_window_location/125_window_location.php:1-8 -L0 (3): EXT_STMT -L1 (3): INIT_FCALL 0 80 string("session_start") -L2 (3): DO_FCALL -L3 (5): EXT_STMT -L4 (5): T3 = FETCH_R (global) string("_GET") -L5 (5): T4 = FETCH_DIM_R T3 string("p1") -L6 (5): V1 = FETCH_W (global) string("_SESSION") -L7 (5): ASSIGN_DIM V1 string("abc") -L8 (5): OP_DATA T4 -L9 (7): EXT_STMT -L10 (7): ECHO string("") -L11 (8): RETURN int(1) -LIVE RANGES: - 4: L6 - L7 (tmp/var) - -$_main: ; (lines=8, args=0, vars=0, tmps=3) - ; (before optimizer) - ; /home/user/gitlab/static-tools---latex/paper_code/PHP/Testability_Patterns/125_JS_redirect/a.php:1-5 -L0 (3): EXT_STMT -L1 (3): INIT_FCALL 0 80 string("session_start") -L2 (3): DO_FCALL -L3 (5): EXT_STMT -L4 (5): T1 = FETCH_R (global) string("_SESSION") -L5 (5): T2 = FETCH_DIM_R T1 string("abc") -L6 (5): ECHO T2 -L7 (5): RETURN int(1) -``` - -- DISCOVERY: - -```bash -SCRIPTING -``` - -- PRECONDITIONS: - 1. - -- TRANSFORMATION: - -``` - -``` - diff --git a/PHP/57_js_redirect/README.md b/PHP/57_js_redirect/README.md new file mode 100755 index 0000000..ebcad82 --- /dev/null +++ b/PHP/57_js_redirect/README.md @@ -0,0 +1,114 @@ +[//]: # (This file is automatically generated. If you wish to make any changes, please use the JSON files and regenerate this file using the tpframework.) + +# Js Redirect + +Tags: sast, php, php_v7.4.9 + +Version: v1.0 + +## Description + +Redirects in Javascript can be done using `window.location`. This patterns tries to find redirects like this. + +## Overview + +| Instances | has discovery rule | discovery method | rule successfull | +|---------------------------|----------------------|--------------------|--------------------| +| [1 Instance](#1-instance) | yes | joern | yes | + +## 1 Instance + +The instance consists of two PHP files, the first file redirects to the second file, that contains the sink. + +### Code + +#### Source File + +```PHP + window.location='1_instance_57_js_redirect_0.php' "; +``` + +#### Sink File + +```PHP + + +More + +
+ + +### Compile + + +```bash +$_main: + ; (lines=9, args=0, vars=0, tmps=5) + ; (before optimizer) + ; /.../PHP/57_js_redirect/1_instance_57_js_redirect/1_instance_57_js_redirect_1.php:1-7 + ; return [] RANGE[0..0] +0000 INIT_FCALL 0 80 string("session_start") +0001 DO_ICALL +0002 T3 = FETCH_R (global) string("_GET") +0003 T4 = FETCH_DIM_R T3 string("p1") +0004 V1 = FETCH_W (global) string("_SESSION") +0005 ASSIGN_DIM V1 string("abc") +0006 OP_DATA T4 +0007 ECHO string("") +0008 RETURN int(1) +LIVE RANGES: + 4: 0004 - 0005 (tmp/var) +``` + +
+ +
+ + +### Discovery + + +The rule searches for a redirect written in JS in the code. + +```scala +// TODO: replace line below with your detection query +val x57 = (name, "57_js_redirect_iall", cpg.call.code(".*