diff --git a/rules/c/security/world-writable-file-c.yml b/rules/c/security/world-writable-file-c.yml new file mode 100644 index 00000000..a514fd7d --- /dev/null +++ b/rules/c/security/world-writable-file-c.yml @@ -0,0 +1,328 @@ +id: world-writable-file-c +language: c +severity: warning +message: >- + This call makes a world-writable file which allows any user on a machine to write to the file. This may allow attackers to influence the behaviour of this process by writing to the file. +note: >- + [CWE-732]: Incorrect Permission Assignment for Critical Resource + [REFERENCES] + - https://wiki.sei.cmu.edu/confluence/display/c/FIO06-C.+Create+files+with+appropriate+access+permissions + +ast-grep-essentials: true + +utils: + follows_umask: + follows: + stopBy: end + kind: expression_statement + has: + kind: call_expression + nthChild: 1 + all: + - has: + nthChild: 1 + kind: identifier + field: function + regex: ^umask$ + - has: + nthChild: 2 + kind: argument_list + field: arguments + + AND_2_EQUALS_2_&_S_IXXXX: + any: + - kind: number_literal + regex: ^-?([2367]|[0-9]*(0[2367]|1[014589]|2[2367]|3[014589]|4[2367]|5[014589]|6[2367]|7[014589]|8[2367]|9[014589]))$ + - all: + - any: + - kind: binary_expression + - kind: identifier + - regex: (\s*S_I[A-Z]{4}\s*\|)*S_I[A-Z]{4} + - regex: .*\bS_IWOTH\b.* + +rule: + any: + # chmod/fchmod/creat + - any: + - matches: AND_2_EQUALS_2_&_S_IXXXX + - kind: identifier + pattern: $MODE + inside: + stopBy: end + follows: + stopBy: end + any: + - kind: declaration + all: + - has: + kind: init_declarator + all: + - has: + kind: identifier + field: declarator + pattern: $MODE + - has: + nthChild: 2 + matches: AND_2_EQUALS_2_&_S_IXXXX + - kind: expression_statement + any: + - has: + kind: assignment_expression + all: + - has: + nthChild: 1 + kind: identifier + pattern: $MODE + - has: + nthChild: 2 + matches: AND_2_EQUALS_2_&_S_IXXXX + - has: + kind: comma_expression + has: + kind: assignment_expression + all: + - has: + nthChild: 1 + kind: identifier + pattern: $MODE + - has: + nthChild: 2 + matches: AND_2_EQUALS_2_&_S_IXXXX + nthChild: + position: 2 + ofRule: + not: + kind: comment + inside: + kind: argument_list + nthChild: 2 + not: + has: + nthChild: + position: 3 + ofRule: + not: + kind: comment + follows: + kind: identifier + regex: ^(chmod|fchmod|creat)$ + inside: + kind: call_expression + not: + any: + - matches: follows_umask + - inside: + stopBy: end + matches: follows_umask + + # fchmodat + - any: + - matches: AND_2_EQUALS_2_&_S_IXXXX + - kind: identifier + pattern: $MODE + inside: + stopBy: end + follows: + stopBy: end + any: + - kind: declaration + all: + - has: + kind: init_declarator + all: + - has: + kind: identifier + field: declarator + pattern: $MODE + - has: + nthChild: 2 + matches: AND_2_EQUALS_2_&_S_IXXXX + - kind: expression_statement + any: + - has: + kind: assignment_expression + all: + - has: + nthChild: 1 + kind: identifier + pattern: $MODE + - has: + nthChild: 2 + matches: AND_2_EQUALS_2_&_S_IXXXX + - has: + kind: comma_expression + has: + kind: assignment_expression + all: + - has: + nthChild: 1 + kind: identifier + pattern: $MODE + - has: + nthChild: 2 + matches: AND_2_EQUALS_2_&_S_IXXXX + nthChild: + position: 3 + ofRule: + not: + kind: comment + inside: + kind: argument_list + nthChild: 2 + follows: + kind: identifier + regex: ^(fchmodat)$ + inside: + kind: call_expression + not: + any: + - matches: follows_umask + - inside: + stopBy: end + matches: follows_umask + + # open + - any: + - matches: AND_2_EQUALS_2_&_S_IXXXX + - kind: identifier + pattern: $MODE + inside: + stopBy: end + follows: + stopBy: end + any: + - kind: declaration + all: + - has: + kind: init_declarator + all: + - has: + kind: identifier + field: declarator + pattern: $MODE + - has: + nthChild: 2 + matches: AND_2_EQUALS_2_&_S_IXXXX + - kind: expression_statement + any: + - has: + kind: assignment_expression + all: + - has: + nthChild: 1 + kind: identifier + pattern: $MODE + - has: + nthChild: 2 + matches: AND_2_EQUALS_2_&_S_IXXXX + - has: + kind: comma_expression + has: + kind: assignment_expression + all: + - has: + nthChild: 1 + kind: identifier + pattern: $MODE + - has: + nthChild: 2 + matches: AND_2_EQUALS_2_&_S_IXXXX + nthChild: + position: 3 + ofRule: + not: + kind: comment + inside: + kind: argument_list + nthChild: 2 + not: + has: + nthChild: + position: 4 + ofRule: + not: + kind: comment + follows: + kind: identifier + regex: ^(open)$ + inside: + kind: call_expression + not: + any: + - matches: follows_umask + - inside: + stopBy: end + matches: follows_umask + + # openat + - any: + - matches: AND_2_EQUALS_2_&_S_IXXXX + - kind: identifier + pattern: $MODE + inside: + stopBy: end + follows: + stopBy: end + any: + - kind: declaration + all: + - has: + kind: init_declarator + all: + - has: + kind: identifier + field: declarator + pattern: $MODE + - has: + nthChild: 2 + matches: AND_2_EQUALS_2_&_S_IXXXX + - kind: expression_statement + any: + - has: + kind: assignment_expression + all: + - has: + nthChild: 1 + kind: identifier + pattern: $MODE + - has: + nthChild: 2 + matches: AND_2_EQUALS_2_&_S_IXXXX + - has: + kind: comma_expression + has: + kind: assignment_expression + all: + - has: + nthChild: 1 + kind: identifier + pattern: $MODE + - has: + nthChild: 2 + matches: AND_2_EQUALS_2_&_S_IXXXX + nthChild: + position: 4 + ofRule: + not: + kind: comment + inside: + kind: argument_list + nthChild: 2 + not: + has: + nthChild: + position: 5 + ofRule: + not: + kind: comment + follows: + kind: identifier + regex: ^(openat)$ + inside: + kind: call_expression + not: + any: + - matches: follows_umask + - inside: + stopBy: end + matches: follows_umask diff --git a/rules/cpp/security/world-writable-file-cpp.yml b/rules/cpp/security/world-writable-file-cpp.yml new file mode 100644 index 00000000..d6bc7177 --- /dev/null +++ b/rules/cpp/security/world-writable-file-cpp.yml @@ -0,0 +1,329 @@ +id: world-writable-file-cpp +language: cpp +severity: warning +message: >- + This call makes a world-writable file which allows any user on a machine to write to the file. This may allow attackers to influence the behaviour of this process by writing to the file. +note: >- + [CWE-732]: Incorrect Permission Assignment for Critical Resource + [REFERENCES] + - https://wiki.sei.cmu.edu/confluence/display/c/FIO06-C.+Create+files+with+appropriate+access+permissions + +ast-grep-essentials: true + +utils: + follows_umask: + follows: + stopBy: end + kind: expression_statement + has: + kind: call_expression + nthChild: 1 + all: + - has: + nthChild: 1 + kind: identifier + field: function + regex: ^umask$ + - has: + nthChild: 2 + kind: argument_list + field: arguments + + AND_2_EQUALS_2_&_S_IXXXX: + any: + - kind: number_literal + regex: ^-?([2367]|[0-9]*(0[2367]|1[014589]|2[2367]|3[014589]|4[2367]|5[014589]|6[2367]|7[014589]|8[2367]|9[014589]))$ + + - all: + - any: + - kind: binary_expression + - kind: identifier + - regex: (\s*S_I[A-Z]{4}\s*\|)*S_I[A-Z]{4} + - regex: .*\bS_IWOTH\b.* + +rule: + any: + # chmod/fchmod/creat + - any: + - matches: AND_2_EQUALS_2_&_S_IXXXX + - kind: identifier + pattern: $MODE + inside: + stopBy: end + follows: + stopBy: end + any: + - kind: declaration + all: + - has: + kind: init_declarator + all: + - has: + kind: identifier + field: declarator + pattern: $MODE + - has: + nthChild: 2 + matches: AND_2_EQUALS_2_&_S_IXXXX + - kind: expression_statement + any: + - has: + kind: assignment_expression + all: + - has: + nthChild: 1 + kind: identifier + pattern: $MODE + - has: + nthChild: 2 + matches: AND_2_EQUALS_2_&_S_IXXXX + - has: + kind: comma_expression + has: + kind: assignment_expression + all: + - has: + nthChild: 1 + kind: identifier + pattern: $MODE + - has: + nthChild: 2 + matches: AND_2_EQUALS_2_&_S_IXXXX + nthChild: + position: 2 + ofRule: + not: + kind: comment + inside: + kind: argument_list + nthChild: 2 + not: + has: + nthChild: + position: 3 + ofRule: + not: + kind: comment + follows: + kind: identifier + regex: ^(chmod|fchmod|creat)$ + inside: + kind: call_expression + not: + any: + - matches: follows_umask + - inside: + stopBy: end + matches: follows_umask + + # fchmodat + - any: + - matches: AND_2_EQUALS_2_&_S_IXXXX + - kind: identifier + pattern: $MODE + inside: + stopBy: end + follows: + stopBy: end + any: + - kind: declaration + all: + - has: + kind: init_declarator + all: + - has: + kind: identifier + field: declarator + pattern: $MODE + - has: + nthChild: 2 + matches: AND_2_EQUALS_2_&_S_IXXXX + - kind: expression_statement + any: + - has: + kind: assignment_expression + all: + - has: + nthChild: 1 + kind: identifier + pattern: $MODE + - has: + nthChild: 2 + matches: AND_2_EQUALS_2_&_S_IXXXX + - has: + kind: comma_expression + has: + kind: assignment_expression + all: + - has: + nthChild: 1 + kind: identifier + pattern: $MODE + - has: + nthChild: 2 + matches: AND_2_EQUALS_2_&_S_IXXXX + nthChild: + position: 3 + ofRule: + not: + kind: comment + inside: + kind: argument_list + nthChild: 2 + follows: + kind: identifier + regex: ^(fchmodat)$ + inside: + kind: call_expression + not: + any: + - matches: follows_umask + - inside: + stopBy: end + matches: follows_umask + + # open + - any: + - matches: AND_2_EQUALS_2_&_S_IXXXX + - kind: identifier + pattern: $MODE + inside: + stopBy: end + follows: + stopBy: end + any: + - kind: declaration + all: + - has: + kind: init_declarator + all: + - has: + kind: identifier + field: declarator + pattern: $MODE + - has: + nthChild: 2 + matches: AND_2_EQUALS_2_&_S_IXXXX + - kind: expression_statement + any: + - has: + kind: assignment_expression + all: + - has: + nthChild: 1 + kind: identifier + pattern: $MODE + - has: + nthChild: 2 + matches: AND_2_EQUALS_2_&_S_IXXXX + - has: + kind: comma_expression + has: + kind: assignment_expression + all: + - has: + nthChild: 1 + kind: identifier + pattern: $MODE + - has: + nthChild: 2 + matches: AND_2_EQUALS_2_&_S_IXXXX + nthChild: + position: 3 + ofRule: + not: + kind: comment + inside: + kind: argument_list + nthChild: 2 + not: + has: + nthChild: + position: 4 + ofRule: + not: + kind: comment + follows: + kind: identifier + regex: ^(open)$ + inside: + kind: call_expression + not: + any: + - matches: follows_umask + - inside: + stopBy: end + matches: follows_umask + + # openat + - any: + - matches: AND_2_EQUALS_2_&_S_IXXXX + - kind: identifier + pattern: $MODE + inside: + stopBy: end + follows: + stopBy: end + any: + - kind: declaration + all: + - has: + kind: init_declarator + all: + - has: + kind: identifier + field: declarator + pattern: $MODE + - has: + nthChild: 2 + matches: AND_2_EQUALS_2_&_S_IXXXX + - kind: expression_statement + any: + - has: + kind: assignment_expression + all: + - has: + nthChild: 1 + kind: identifier + pattern: $MODE + - has: + nthChild: 2 + matches: AND_2_EQUALS_2_&_S_IXXXX + - has: + kind: comma_expression + has: + kind: assignment_expression + all: + - has: + nthChild: 1 + kind: identifier + pattern: $MODE + - has: + nthChild: 2 + matches: AND_2_EQUALS_2_&_S_IXXXX + nthChild: + position: 4 + ofRule: + not: + kind: comment + inside: + kind: argument_list + nthChild: 2 + not: + has: + nthChild: + position: 5 + ofRule: + not: + kind: comment + follows: + kind: identifier + regex: ^(openat)$ + inside: + kind: call_expression + not: + any: + - matches: follows_umask + - inside: + stopBy: end + matches: follows_umask diff --git a/tests/__snapshots__/world-writable-file-c-snapshot.yml b/tests/__snapshots__/world-writable-file-c-snapshot.yml new file mode 100644 index 00000000..10da9622 --- /dev/null +++ b/tests/__snapshots__/world-writable-file-c-snapshot.yml @@ -0,0 +1,79 @@ +id: world-writable-file-c +snapshots: + ? | + void test_octal_bad() { + mode_t mode = 0666; + chmod("/tmp/foo", mode); + int fd = open_log(); + fchmod(fd, mode); + int dirfd = open_log_dir(); + fchmodat(dirfd, "log", mode, AT_SYMLINK_NOFOLLOW); + open("log", O_CREAT, mode); + openat(fd, "log", O_CREAT, mode); + creat("log", mode); + } + : labels: + - source: mode + style: primary + start: 66 + end: 70 + - source: mode + style: secondary + start: 33 + end: 37 + - source: '0666' + style: secondary + start: 40 + end: 44 + - source: mode = 0666 + style: secondary + start: 33 + end: 44 + - source: mode_t mode = 0666; + style: secondary + start: 26 + end: 45 + - source: mode_t mode = 0666; + style: secondary + start: 26 + end: 45 + - source: chmod("/tmp/foo", mode) + style: secondary + start: 48 + end: 71 + - source: chmod + style: secondary + start: 48 + end: 53 + - source: ("/tmp/foo", mode) + style: secondary + start: 53 + end: 71 + ? | + void test_symbol_direct_bad() { + chmod("/tmp/foo", S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH); + int fd = open_log(); + fchmod(fd, S_IROTH | S_IWOTH | S_IRUSR | S_IWUSR); + int dirfd = open_log_dir(); + fchmodat(dirfd, "log", S_IWOTH); + open("log", O_CREAT, S_IWUSR | S_IWOTH); + openat(fd, "log", O_CREAT, S_IWOTH | S_IUSR | S_IGRP); + creat("log", S_IWOTH); + } + : labels: + - source: S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH + style: primary + start: 52 + end: 109 + - source: chmod("/tmp/foo", S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH) + style: secondary + start: 34 + end: 110 + - source: chmod + style: secondary + start: 34 + end: 39 + - source: ("/tmp/foo", S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH) + style: secondary + start: 39 + end: 110 diff --git a/tests/__snapshots__/world-writable-file-cpp-snapshot.yml b/tests/__snapshots__/world-writable-file-cpp-snapshot.yml new file mode 100644 index 00000000..54f1344e --- /dev/null +++ b/tests/__snapshots__/world-writable-file-cpp-snapshot.yml @@ -0,0 +1,79 @@ +id: world-writable-file-cpp +snapshots: + ? | + void test_octal_bad() { + mode_t mode = 0666; + chmod("/tmp/foo", mode); + int fd = open_log(); + fchmod(fd, mode); + int dirfd = open_log_dir(); + fchmodat(dirfd, "log", mode, AT_SYMLINK_NOFOLLOW); + open("log", O_CREAT, mode); + openat(fd, "log", O_CREAT, mode); + creat("log", mode); + } + : labels: + - source: mode + style: primary + start: 66 + end: 70 + - source: mode + style: secondary + start: 33 + end: 37 + - source: '0666' + style: secondary + start: 40 + end: 44 + - source: mode = 0666 + style: secondary + start: 33 + end: 44 + - source: mode_t mode = 0666; + style: secondary + start: 26 + end: 45 + - source: mode_t mode = 0666; + style: secondary + start: 26 + end: 45 + - source: chmod("/tmp/foo", mode) + style: secondary + start: 48 + end: 71 + - source: chmod + style: secondary + start: 48 + end: 53 + - source: ("/tmp/foo", mode) + style: secondary + start: 53 + end: 71 + ? | + void test_symbol_direct_bad() { + chmod("/tmp/foo", S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH); + int fd = open_log(); + fchmod(fd, S_IROTH | S_IWOTH | S_IRUSR | S_IWUSR); + int dirfd = open_log_dir(); + fchmodat(dirfd, "log", S_IWOTH); + open("log", O_CREAT, S_IWUSR | S_IWOTH); + openat(fd, "log", O_CREAT, S_IWOTH | S_IUSR | S_IGRP); + creat("log", S_IWOTH); + } + : labels: + - source: S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH + style: primary + start: 52 + end: 109 + - source: chmod("/tmp/foo", S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH) + style: secondary + start: 34 + end: 110 + - source: chmod + style: secondary + start: 34 + end: 39 + - source: ("/tmp/foo", S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH) + style: secondary + start: 39 + end: 110 diff --git a/tests/c/world-writable-file-c-test.yml b/tests/c/world-writable-file-c-test.yml new file mode 100644 index 00000000..5053e399 --- /dev/null +++ b/tests/c/world-writable-file-c-test.yml @@ -0,0 +1,37 @@ +id: world-writable-file-c +valid: + - | + void test_symbol_direct_good() { + chmod("/tmp/foo", S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH); + int fd = open_log(); + fchmod(fd, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH); + int dirfd = open_log_dir(); + fchmodat(dirfd, "log", S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH, AT_SYMLINK_NOFOLLOW); + open("log", O_CREAT, mode); + openat(fd, "log", O_CREAT, mode); + creat("log", mode); + } +invalid: + - | + void test_octal_bad() { + mode_t mode = 0666; + chmod("/tmp/foo", mode); + int fd = open_log(); + fchmod(fd, mode); + int dirfd = open_log_dir(); + fchmodat(dirfd, "log", mode, AT_SYMLINK_NOFOLLOW); + open("log", O_CREAT, mode); + openat(fd, "log", O_CREAT, mode); + creat("log", mode); + } + - | + void test_symbol_direct_bad() { + chmod("/tmp/foo", S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH); + int fd = open_log(); + fchmod(fd, S_IROTH | S_IWOTH | S_IRUSR | S_IWUSR); + int dirfd = open_log_dir(); + fchmodat(dirfd, "log", S_IWOTH); + open("log", O_CREAT, S_IWUSR | S_IWOTH); + openat(fd, "log", O_CREAT, S_IWOTH | S_IUSR | S_IGRP); + creat("log", S_IWOTH); + } diff --git a/tests/cpp/world-writable-file-cpp-test.yml b/tests/cpp/world-writable-file-cpp-test.yml new file mode 100644 index 00000000..9892771a --- /dev/null +++ b/tests/cpp/world-writable-file-cpp-test.yml @@ -0,0 +1,37 @@ +id: world-writable-file-cpp +valid: + - | + void test_symbol_direct_good() { + chmod("/tmp/foo", S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH); + int fd = open_log(); + fchmod(fd, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH); + int dirfd = open_log_dir(); + fchmodat(dirfd, "log", S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH, AT_SYMLINK_NOFOLLOW); + open("log", O_CREAT, mode); + openat(fd, "log", O_CREAT, mode); + creat("log", mode); + } +invalid: + - | + void test_octal_bad() { + mode_t mode = 0666; + chmod("/tmp/foo", mode); + int fd = open_log(); + fchmod(fd, mode); + int dirfd = open_log_dir(); + fchmodat(dirfd, "log", mode, AT_SYMLINK_NOFOLLOW); + open("log", O_CREAT, mode); + openat(fd, "log", O_CREAT, mode); + creat("log", mode); + } + - | + void test_symbol_direct_bad() { + chmod("/tmp/foo", S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH); + int fd = open_log(); + fchmod(fd, S_IROTH | S_IWOTH | S_IRUSR | S_IWUSR); + int dirfd = open_log_dir(); + fchmodat(dirfd, "log", S_IWOTH); + open("log", O_CREAT, S_IWUSR | S_IWOTH); + openat(fd, "log", O_CREAT, S_IWOTH | S_IUSR | S_IGRP); + creat("log", S_IWOTH); + }