5 | references:
6 | - https://github.com/0xdea/ghidra-scripts/blob/main/Rhabdomancer.java
7 | - https://github.com/x509cert/banned/blob/master/banned.h
8 | - https://g.co/kgs/PCHQjJ
9 | - https://www.sei.cmu.edu/downloads/sei-cert-c-coding-standard-2016-v01.pdf
10 | confidence: MEDIUM
11 | # NOTE: goto, try/catch, kill/sig/jmp, sem/mutex, new/delete,
12 | # static_cast/reinterpret_cast are not covered.
13 | source: https://github.com/brave/security-action/blob/main/assets/semgrep_rules/client/interesting-api-calls.yaml
14 | category: security
15 | message: >-
16 | Locate all calls to interesting and potentially insecure API functions (candidate points). The auditor can backtrace from these candidate points to find pathways allowing access from untrusted input.
17 | severity: INFO
18 | languages:
19 | - c
20 | - cpp
21 | patterns:
22 | - pattern: $FUNC(...)
23 | - metavariable-regex:
24 | metavariable: $FUNC
25 | regex: \w*(set\w*(u|g)id|(init|set)groups|str\w?cpy|stpn?cpy|str\w?cat|wcs\w?cpy|wcpn?cpy|wcs\w?cat|strtok|wcstok|s\w?printf\w*\(.*|sn\w?printf\w*\(.*|scanf|get(s|c|char|pw|pass|wd|cwd|env|opt|opt_long)|memc?cpy|mem(move|set)|bcopy|alloca|exec(l|v)?(p|e)?e?|system|open(at)?(64)?|pipe|connect|read|recv(from)?|fork|clone|mk\w?temp(64)?|te?mpnam|tmpfile|mkdir|creat|link(at)?|rename(at)?|access(at)?|stat(at)?|ch(own|mod)(at)?|rand|assert)$
26 | - pattern-not-regex: RunOnUIThread
27 | - pattern-not-regex: CalledOnValidThread
28 |
--------------------------------------------------------------------------------
/assets/semgrep_rules/client/licensing.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | The following JavaScript library
4 |
5 | by
6 |
7 | John Doe
8 |
9 | is licensed under
10 |
11 | // ruleid: license-nonfree
12 | CC BY-NC 4.0
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/assets/semgrep_rules/client/licensing.txt:
--------------------------------------------------------------------------------
1 | // ruleid: license-nonfree
2 | This logo is licensed under the Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International license.
3 |
4 | See LICENSE.txt for more details.
5 |
--------------------------------------------------------------------------------
/assets/semgrep_rules/client/licensing.yaml:
--------------------------------------------------------------------------------
1 | rules:
2 | - id: license-nonfree
3 | metadata:
4 | author: Francois Marier
5 | confidence: LOW
6 | assignees: |
7 | diracdeltas
8 | fmarier
9 | thypon
10 | source: https://github.com/brave/security-action/blob/main/assets/semgrep_rules/client/licensing.yaml
11 | category: security
12 | message: "Non-free license detected"
13 | severity: ERROR
14 | languages:
15 | - regex
16 | patterns:
17 | # ruleid: license-nonfree
18 | - pattern-regex: (NonCommercial|NoDerivs|BY-NC|BY-ND)
19 |
--------------------------------------------------------------------------------
/assets/semgrep_rules/client/mismatched-memory-management-cpp.cpp:
--------------------------------------------------------------------------------
1 | // ok: raptor-mismatched-memory-management-cpp
2 | Blah& operator=(const Blah&) = delete;
3 |
4 | void RedeemOptedOutConfirmation::Destroy() {
5 | // ok: raptor-mismatched-memory-management-cpp
6 | delete this;
7 | }
8 |
9 | void bad1()
10 | {
11 | BarObj *ptr = new BarObj()
12 |
13 | // ruleid: raptor-mismatched-memory-management-cpp
14 | free(ptr);
15 | }
16 |
17 | void good1()
18 | {
19 | BarObj *ptr = new BarObj()
20 |
21 | // ok: raptor-mismatched-memory-management-cpp
22 | delete ptr;
23 | }
24 |
25 | class A {
26 | void bad2();
27 | void good2();
28 | };
29 |
30 | void A::bad2()
31 | {
32 | int *ptr;
33 | ptr = (int*)malloc(sizeof(int));
34 |
35 | // ruleid: raptor-mismatched-memory-management-cpp
36 | delete ptr;
37 | }
38 |
39 | void A::good2()
40 | {
41 | int *ptr;
42 | ptr = (int*)malloc(sizeof(int));
43 |
44 | // ok: raptor-mismatched-memory-management-cpp
45 | free(ptr);
46 | }
47 |
48 | class B {
49 | void bad3(bool);
50 | void good3();
51 | };
52 |
53 | void B::bad3(bool heap) {
54 | int localArray[2] = { 11,22 };
55 | int *p = localArray;
56 |
57 | if (heap) {
58 | p = new int[2];
59 | }
60 |
61 | // ruleid: raptor-mismatched-memory-management-cpp
62 | delete[] p;
63 | }
64 |
65 | void B::good3() {
66 | int localArray[2] = { 11,22 };
67 | int *p = localArray;
68 |
69 | p = new (std::nothrow) int[2];
70 |
71 | // ok: raptor-mismatched-memory-management-cpp
72 | delete[] p;
73 | }
--------------------------------------------------------------------------------
/assets/semgrep_rules/client/mismatched-memory-management-cpp.yaml:
--------------------------------------------------------------------------------
1 | rules:
2 | - id: raptor-mismatched-memory-management-cpp
3 | metadata:
4 | author: Marco Ivaldi
5 | references:
6 | - https://cwe.mitre.org/data/definitions/762
7 | - https://cwe.mitre.org/data/definitions/590
8 | - https://github.com/struct/mms
9 | - https://docs.microsoft.com/en-us/cpp/sanitizers/asan-error-examples
10 | confidence: LOW
11 | # NOTE: valloc(), reallocf(), aligned_alloc(), and custom wrappers
12 | # are not covered.
13 | # NOTE: overloaded operators, VirtualAlloc()/VirtualFree(),
14 | # mmap()/munmap() are not covered.
15 | source: https://github.com/brave/security-action/blob/main/assets/semgrep_rules/client/mismatched-memory-management-cpp.yaml
16 | category: security
17 | message: >-
18 | The software attempts to return a memory resource to the system, but it calls a release function that is not compatible with the function that was originally used to allocate that resource. When the memory management functions are mismatched, the consequences may be as severe as code execution, memory corruption, or program crash. Consequences and ease of exploit will vary depending on the implementation of the routines and the object being managed. Due to inherent limitations of Semgrep, this rule might generate many false positives and should therefore be customized for your codebase.
19 | severity: INFO
20 | languages:
21 | - cpp
22 | pattern-either:
23 | # free
24 | - patterns:
25 | - pattern: free($PTR);
26 | - pattern-not-inside: |
27 | $PTR = malloc(...);
28 | ...
29 | free($PTR);
30 | - pattern-not-inside: |
31 | $PTR = ($CAST)malloc(...);
32 | ...
33 | free($PTR);
34 | - pattern-not-inside: |
35 | $PTR = calloc(...);
36 | ...
37 | free($PTR);
38 | - pattern-not-inside: |
39 | $PTR = ($CAST)calloc(...);
40 | ...
41 | free($PTR);
42 | - pattern-not-inside: |
43 | $PTR = realloc(...);
44 | ...
45 | free($PTR);
46 | - pattern-not-inside: "$PTR = ($CAST)realloc(...);\n...\nfree($PTR); \n"
47 | - pattern-not-inside: "$PTR = strdup(...);\n...\nfree($PTR); \n"
48 | - pattern-not-inside: "$PTR = strndup(...);\n...\nfree($PTR); \n"
49 | # delete[]
50 | - patterns:
51 | - pattern: delete[]($PTR);
52 | - pattern-not-inside: |
53 | $PTR = new $OBJ[$SIZE];
54 | ...
55 | delete[]($PTR);
56 | - pattern-not: delete[](this);
57 | - metavariable-regex:
58 | metavariable: $PTR
59 | regex: .
60 | # delete
61 | - patterns:
62 | - pattern: delete($PTR);
63 | - pattern-not-inside: |
64 | $PTR = new $OBJ;
65 | ...
66 | delete($PTR);
67 | - pattern-not: delete(this);
68 | - metavariable-regex:
69 | metavariable: $PTR
70 | regex: .
71 | - patterns:
72 | - pattern: delete($PTR);
73 | - pattern-inside: |
74 | $PTR = new $OBJ[$SIZE];
75 | ...
76 | delete($PTR);
77 | - pattern-not: delete(this);
78 | - metavariable-regex:
79 | metavariable: $PTR
80 | regex: .
81 |
--------------------------------------------------------------------------------
/assets/semgrep_rules/client/privacy.en.md:
--------------------------------------------------------------------------------
1 | // ruleid: privacy
2 | test completely private
3 | // ruleid: privacy
4 | test military grade
5 | // ruleid: privacy
6 | test military-grade
7 | // ruleid: privacy
8 | test totally secure
9 | // ruleid: privacy
10 | test unbreakable encryption
11 | // ruleid: privacy
12 | test unhackable
13 | // ruleid: privacy
14 | test hackerproof
15 | // ruleid: privacy
16 | test hacker-proof
17 |
--------------------------------------------------------------------------------
/assets/semgrep_rules/client/privacy.md:
--------------------------------------------------------------------------------
1 | // ruleid: privacy
2 | test completely private
3 | // ruleid: privacy
4 | test military grade
5 | // ruleid: privacy
6 | test military-grade
7 | // ruleid: privacy
8 | test totally secure
9 | // ruleid: privacy
10 | test unbreakable encryption
11 | // ruleid: privacy
12 | test unhackable
13 | // ruleid: privacy
14 | test hackerproof
15 | // ruleid: privacy
16 | test hacker-proof
17 |
--------------------------------------------------------------------------------
/assets/semgrep_rules/client/refcounted-usage.cpp:
--------------------------------------------------------------------------------
1 | // ruleid: refcounted-usage
2 | class MyClass : public base::RefCounted {
3 | };
4 |
5 | // ruleid: refcounted-usage
6 | class ThreadSafeClass : public base::RefCountedThreadSafe {
7 | };
8 |
9 | // ruleid: refcounted-usage
10 | base::RefCountedData shared_integer(42);
11 |
12 | // ok: refcounted-usage
13 | class RegularClass {
14 | };
15 |
16 | // ruleid: refcounted-usage
17 | using MyRefCountedType = base::RefCounted;
18 |
19 | // ruleid: refcounted-usage
20 | class NestedRefCounted : public base::RefCountedThreadSafe {
21 | // ruleid: refcounted-usage
22 | base::RefCountedData nested_data_;
23 | };
24 |
--------------------------------------------------------------------------------
/assets/semgrep_rules/client/refcounted-usage.yaml:
--------------------------------------------------------------------------------
1 | rules:
2 | - id: refcounted-usage
3 | metadata:
4 | author: Artem Chaikin
5 | source: https://github.com/brave/security-action/blob/main/assets/semgrep_rules/client/refcounted-usage.yaml
6 | assignees: |
7 | stoletheminerals
8 | cdesouza-chromium
9 | bridiver
10 | pattern-either:
11 | - pattern: base::RefCounted<...>
12 | - pattern: base::RefCountedThreadSafe<...>
13 | - pattern: base::RefCountedData<...>
14 | message: "Reference counting is occasionally useful but is more often a sign that someone isn't thinking carefully about ownership. Use it when ownership is truly shared (for example, multiple tabs sharing the same renderer process), not for when lifetime management is difficult to reason about."
15 | languages:
16 | - generic
17 | paths:
18 | include:
19 | - "*.c"
20 | - "*.cpp"
21 | - "*.cc"
22 | - "*.h"
23 | - "*.hh"
24 | - "*.hcc"
25 | - "*.mm"
26 | severity: WARNING
27 |
--------------------------------------------------------------------------------
/assets/semgrep_rules/client/reinterpret_cast.cpp:
--------------------------------------------------------------------------------
1 | // ruleid: reinterpret_cast
2 | std::string_view der_cert(reinterpret_cast(cert->pbCertEncoded), cert->cbCertEncoded);
3 | // ruleid: reinterpret_cast
4 | const uint8_t* string_data =reinterpret_cast(response_body.data());
5 | // ruleid: reinterpret_cast
6 | uint32_t value = *reinterpret_cast(bytes.data());
7 | // ruleid: reinterpret_cast
8 | int rv = PKCS5_PBKDF2_HMAC(mnemonic.data(), mnemonic.length(), reinterpret_cast(salt.data()), salt.length(), 2048, EVP_sha512(),seed->size(), seed->data());
9 | // ruleid: reinterpret_cast
10 | float* float_data = reinterpret_cast(const_cast(data));
11 | // ok: reinterpret_cast
12 | auto orig_fn = reinterpret_cast(g_originals.functions[GET_MODULE_FILENAME_EX_W_ID]);
13 | // ruleid: reinterpret_cast
14 | size_t bytes_read = reinterpret_cast(arg0);
15 |
--------------------------------------------------------------------------------
/assets/semgrep_rules/client/reinterpret_cast.yaml:
--------------------------------------------------------------------------------
1 | rules:
2 | - id: reinterpret_cast
3 | metadata:
4 | author: Artem Chaikin
5 | references:
6 | - https://chromium.googlesource.com/chromium/src/+/main/docs/unsafe_buffers.md#Avoid-reinterpret_cast
7 | source: https://github.com/brave/security-action/blob/main/assets/semgrep_rules/client/reinterpret_cast.yaml
8 | assignees: |
9 | stoletheminerals
10 | thypon
11 | cdesouza-chromium
12 | languages: [cpp]
13 | message: "Using `reinterpret_cast` against some data types may lead to undefined behaviour. In general, when needing to do these conversions, check how Chromium upstream does them. Most of the times a reinterpret_cast is wrong and there's no guarantee the compiler will generate the code that you thought it would."
14 | severity: WARNING
15 | patterns:
16 | - pattern: reinterpret_cast<$T>($ARG)
17 | - metavariable-regex:
18 | metavariable: $T
19 | regex: ^(.*int.*|.*double.*|.*float.*|.*char.*|.*size_t.*)$ # this probably needs to be tweaked
20 |
--------------------------------------------------------------------------------
/assets/semgrep_rules/client/typos.c:
--------------------------------------------------------------------------------
1 | // original author: Marco Ivaldi
2 | // original source: https://github.com/0xdea/semgrep-rules/blob/main/c/typos.c
3 | // LICENSE: MIT
4 | // author: Andrea Brancaleoni
5 |
6 | #include
7 | #include
8 | #include
9 |
10 | int bad1()
11 | {
12 | char *src, *dst;
13 | int left;
14 |
15 | while (*src && left) {
16 | *dst++=*src++;
17 | // ruleid: raptor-typos
18 | if (left = 0) {
19 | die("badlen");
20 | }
21 | left--;
22 | }
23 | }
24 |
25 | void
26 | good1(char *path, char *dir, char *obj)
27 | {
28 | char *last;
29 |
30 | // ok: raptor-typos
31 | if ( (last = strrchr(path,'/')) != NULL ) {
32 | strcpy(obj, last + 1);
33 | if (last == path) {
34 | strcpy(dir, "/");
35 | } else {
36 | *last = '\0';
37 | strcpy(dir, path);
38 | *last = '/';
39 | }
40 | } else {
41 | dir[0] = dir[0] = '\0';
42 | }
43 | }
44 |
45 | void
46 | good2(char *path, char *dir, char *obj)
47 | {
48 | char *last;
49 |
50 | // ok: raptor-typos
51 | if (char *last = strrchr(path,'/' )) {
52 | strcpy(obj, last + 1);
53 | if (last == path) {
54 | strcpy(dir, "/");
55 | } else {
56 | *last = '\0';
57 | strcpy(dir, path);
58 | *last = '/';
59 | }
60 | } else {
61 | dir[0] = dir[0] = '\0';
62 | }
63 | }
64 |
65 | int bad2(char *username)
66 | {
67 | int f;
68 | f = get_security_flags(username);
69 |
70 | // ruleid: raptor-typos
71 | if (f = FLAG_AUTHENTICATED) {
72 | return LOGIN_OK;
73 | }
74 | return LOGIN_FAILED;
75 | }
76 |
77 | int bad3(char *src, char *dst)
78 | {
79 | // ok: raptor-typos
80 | if (get_string(src) &&
81 | check_for_overflow(src) & copy_string(dst, src)) {
82 | printf("string safely copied\n");
83 | }
84 | }
85 |
86 | int bad4(char *src, int len)
87 | {
88 | char dst[256];
89 |
90 | // ruleid: raptor-typos
91 | if (len > 0 && len <= sizeof(dst));
92 | memcpy(dst, src, len);
93 | }
94 |
95 | int bad5(char *src, char *dst)
96 | {
97 | int i;
98 | // ruleid: raptor-typos
99 | for (i == 5; src[i] && i < 10; i++) {
100 | dst[i - 5] = src[i];
101 | }
102 | }
103 |
104 | int bad6(char *userinput)
105 | {
106 | // ruleid: raptor-typos
107 | char buf[040];
108 |
109 | snprintf(buf, 40, "%s", userinput);
110 | }
111 |
112 | int bad7()
113 | {
114 | // ok: raptor-typos
115 | if (frag_len &
116 | !BUF_MEM_grow_clean(s->init_buf, (int)frag_len +
117 | DTLS1_HM_HEADER_LENGTH + s->init_num)) {
118 | SSLerr(SSL_F_DTLS1_GET_MESSAGE_FRAGMENT, ERR_R_BUF_LIB);
119 | goto err;
120 | }
121 | }
122 |
123 | int bad8(int j)
124 | {
125 | int i = 10;
126 |
127 | // ruleid: raptor-typos
128 | i =+ j;
129 | }
130 |
131 | int isValid(int value)
132 | {
133 | // ruleid: raptor-typos
134 | if (value = 100) {
135 | printf("Value is valid\n");
136 | return(1);
137 | }
138 |
139 | printf("Value is not valid\n");
140 | return(0);
141 | }
142 |
143 | void processString (char *str)
144 | {
145 | int i;
146 |
147 | for(i = 0; i < strlen(str); i++) {
148 | if (isalnum(str[i])){
149 | processChar(str[i]);
150 | // ruleid: raptor-typos
151 | } else if (str[i] = ':') {
152 | movingToNewInput();
153 | }
154 | }
155 | }
156 |
157 | #define SIZE 50
158 | int *tos, *p1, stack[SIZE];
159 |
160 | void push(int i)
161 | {
162 | p1++;
163 | if (p1 == (tos + SIZE)) {
164 | printf("Print stack overflow error message and exit\n");
165 | }
166 | // ok: raptor-typos
167 | *p1 == i;
168 | }
169 |
170 | int pop(void)
171 | {
172 | if (p1 == tos) {
173 | printf("Print stack underflow error message and exit\n");
174 | }
175 | p1--;
176 | return *(p1 + 1);
177 | }
178 |
179 | int main(int argc, char *argv[])
180 | {
181 | tos = stack;
182 | p1 = stack;
183 | // ...
184 | return 0;
185 | }
186 | // ok: raptor-typos
187 | void TearDown() { settings_map_->ShutdownOnUIThread(); }
188 |
--------------------------------------------------------------------------------
/assets/semgrep_rules/client/typos.cc:
--------------------------------------------------------------------------------
1 | // ok: raptor-typos
2 | if (auto* playlist_service =
3 | playlist::PlaylistServiceFactory::GetForBrowserContext(
4 | web_contents->GetBrowserContext())) {
5 | playlist_service->ConfigureWebPrefsForBackgroundWebContents(web_contents,
6 | web_prefs);
7 | }
8 |
9 | if (host_content_settings_map) {
10 | // ok: raptor-typos
11 | if (std::unique_ptr
12 | domain_block_navigation_throttle = brave_shields::
13 | DomainBlockNavigationThrottle::MaybeCreateThrottleFor(
14 | handle, g_brave_browser_process->ad_block_service(),
15 | g_brave_browser_process->ad_block_service()
16 | ->custom_filters_provider(),
17 | EphemeralStorageServiceFactory::GetForContext(context),
18 | host_content_settings_map,
19 | g_browser_process->GetApplicationLocale())) {
20 | throttles.push_back(std::move(domain_block_navigation_throttle));
21 | }
22 | }
23 |
24 | content::StoragePartitionConfig
25 | BraveContentBrowserClient::GetStoragePartitionConfigForSite(
26 | content::BrowserContext* browser_context,
27 | const GURL& site) {
28 | // ok: raptor-typos
29 | if (auto* request_otr_service =
30 | request_otr::RequestOTRServiceFactory::GetForBrowserContext(
31 | browser_context)) {
32 | if (request_otr_service->IsOTR(site)) {
33 | CHECK(site.has_host()); // upstream also does this before accessing
34 | // site.host()
35 | return content::StoragePartitionConfig::Create(
36 | browser_context, site.host(), /*partition_name=*/"request_otr",
37 | /*in_memory=*/true);
38 | }
39 |
40 | // ruleid: raptor-typos
41 | if (request_otr_service =
42 | request_otr::RequestOTRServiceFactory::GetForBrowserContext(
43 | browser_context)) {
44 | if (request_otr_service->IsOTR(site)) {
45 | CHECK(site.has_host());
46 | }
47 |
48 |
49 | }
50 |
51 | return ChromeContentBrowserClient::GetStoragePartitionConfigForSite(
52 | browser_context, site);
53 | }
54 |
--------------------------------------------------------------------------------
/assets/semgrep_rules/client/typos.yaml:
--------------------------------------------------------------------------------
1 | rules:
2 | - id: raptor-typos
3 | metadata:
4 | author: Andrea Brancaleoni
5 | original_author: Marco Ivaldi
6 | references:
7 | - https://cwe.mitre.org/data/definitions/480
8 | - https://cwe.mitre.org/data/definitions/481
9 | - https://cwe.mitre.org/data/definitions/482
10 | - https://cwe.mitre.org/data/definitions/483
11 | - https://g.co/kgs/PCHQjJ
12 | - https://www.sei.cmu.edu/downloads/sei-cert-c-coding-standard-2016-v01.pdf
13 | confidence: LOW
14 | license: MIT
15 | original_source: https://raw.githubusercontent.com/0xdea/semgrep-rules/main/c/typos.yaml
16 | source: https://github.com/brave/security-action/blob/main/assets/semgrep_rules/client/typos.yaml
17 | category: security
18 | # NOTE: common issues with comments are not covered.
19 | # NOTE: constructs such as assert(var == val) lead to false positives.
20 | message: >-
21 | The programmer accidentally uses the wrong operator, which changes the application logic in security-relevant ways. These types of errors are generally the result of a typo. This rule also covers some other common typo patterns. (see $EXPR1)
22 | severity: WARNING
23 | languages:
24 | - c
25 | - cpp
26 | pattern-either:
27 | # == instead of = in assignment (the ternary operator is not supported by Semgrep)
28 | - pattern: for ($EXPR1 == $EXPR2; $EXPR3; $EXPR4) ...
29 | # NOTE: removed since anything similar to DCHECK leads to false positive here
30 | # - pattern: $EXPR1 == $EXPR2;
31 | # = instead of == in comparison
32 | - patterns:
33 | - pattern: if (<... $EXPR1 = $EXPR2 ...>) ...
34 | - pattern-not-inside: if (<... ($EXPR1 = $EXPR2) == $EXPR ...>) ...
35 | - pattern-not-inside: if (<... ($EXPR1 = $EXPR2) != $EXPR ...>) ...
36 | - pattern-not-inside: if (<... ($EXPR1 = $EXPR2) < $EXPR ...>) ...
37 | - pattern-not-inside: if (<... ($EXPR1 = $EXPR2) <= $EXPR ...>) ...
38 | - pattern-not-inside: if (<... ($EXPR1 = $EXPR2) > $EXPR ...>) ...
39 | - pattern-not-inside: if (<... ($EXPR1 = $EXPR2) >= $EXPR ...>) ...
40 | - pattern-not-regex: "if\\s*\\(\\s*[_a-zA-Z][_a-zA-Z0-9:<>]{0,40}\\**\\s+\\**[_a-zA-Z][_a-zA-Z0-9]{0,30}\\s*"
41 | - pattern-not-regex: "if\\s*\\(\\s*[_a-zA-Z][_a-zA-Z0-9*:]{0,40}<[_a-zA-Z][_a-zA-Z0-9:]{0,30}>\\s*"
42 | # REMOVED: False Positive on Chrome, & instead of && in comparison
43 | # - patterns:
44 | # - pattern: if (<... $EXPR1 & $EXPR2 ...>) ...
45 | # - pattern-not-inside: if (<... ($EXPR1 & $EXPR2) == $EXPR ...>) ...
46 | # - pattern-not-inside: if (<... ($EXPR1 & $EXPR2) != $EXPR ...>) ...
47 | # - pattern-not-inside: if (<... ($EXPR1 & $EXPR2) < $EXPR ...>) ...
48 | # - pattern-not-inside: if (<... ($EXPR1 & $EXPR2) <= $EXPR ...>) ...
49 | # - pattern-not-inside: if (<... ($EXPR1 & $EXPR2) > $EXPR ...>) ...
50 | # - pattern-not-inside: if (<... ($EXPR1 & $EXPR2) >= $EXPR ...>) ...
51 | # REMOVED: False Positive on Chrome, | instead of || in comparison
52 | # - patterns:
53 | # - pattern: if (<... $EXPR1 | $EXPR2 ...>) ...
54 | # - pattern-not-inside: if (<... ($EXPR1 | $EXPR2) == $EXPR ...>) ...
55 | # - pattern-not-inside: if (<... ($EXPR1 | $EXPR2) != $EXPR ...>) ...
56 | # - pattern-not-inside: if (<... ($EXPR1 | $EXPR2) < $EXPR ...>) ...
57 | # - pattern-not-inside: if (<... ($EXPR1 | $EXPR2) <= $EXPR ...>) ...
58 | # - pattern-not-inside: if (<... ($EXPR1 | $EXPR2) > $EXPR ...>) ...
59 | # - pattern-not-inside: if (<... ($EXPR1 | $EXPR2) >= $EXPR ...>) ...
60 | # =+ instead of += (and =- instead of -=)
61 | - pattern: $EXPR1 =+ $EXPR2
62 | # - pattern: $EXPR1 =- $EXPR2
63 | # ; at the end of if() or for() statement
64 | - pattern: if ($COND);
65 | - pattern: for ($EXPR1; $EXPR2; $EXPR3);
66 | # accidental octal conversion
67 | - patterns:
68 | - pattern-either:
69 | - pattern: $TYPE $ARR[$SIZE];
70 | - pattern: $TYPE $ARR[$SIZE] = $EXPR;
71 | - metavariable-regex:
72 | metavariable: $SIZE
73 | regex: '^0.*'
74 | # - pattern: if ($COND) $BODY;
75 |
--------------------------------------------------------------------------------
/assets/semgrep_rules/client/unsafe-cpp-constructs.cpp:
--------------------------------------------------------------------------------
1 | #ifdef UNSAFE_BUFFERS_BUILD
2 | // TODO(): Remove this and
3 | // convert code to safer constructs.
4 | // ruleid: unsafe_cpp_constructs
5 | #pragma allow_unsafe_buffers
6 | #endif
7 | // ruleid: unsafe_cpp_constructs
8 | std::next(it);
9 | // ruleid: unsafe_cpp_constructs
10 | std::advance(cert_iter, cert_idx);
11 | // ruleid: unsafe_cpp_constructs
12 | std::prev(it);
13 | // ruleid: unsafe_cpp_constructs
14 | const void* const kUserDataKey = &kUserDataKey;
15 | // ok: unsafe_cpp_constructs
16 | static void RegisterCallback(AtExitCallbackType func, uint8_t param);
17 |
--------------------------------------------------------------------------------
/assets/semgrep_rules/client/unsafe-cpp-constructs.yaml:
--------------------------------------------------------------------------------
1 | rules:
2 | - id: unsafe_cpp_constructs
3 | metadata:
4 | author: Artem Chaikin
5 | references:
6 | - https://github.com/brave/brave-browser/wiki/Security-reviews
7 | source: https://github.com/brave/security-action/blob/main/assets/semgrep_rules/client/unsafe-cpp-constructs.yaml
8 | assignees: |
9 | stoletheminerals
10 | thypon
11 | cdesouza-chromium
12 | category: security
13 | languages: [cpp]
14 | message: "Potentially unsafe C++ construct detected"
15 | severity: WARNING
16 | patterns:
17 | - pattern-either:
18 | - pattern: "std::next(...)"
19 | - pattern: "std::advance(...)"
20 | - pattern: "std::prev(...)"
21 | - pattern-regex: "void\\*"
22 | - pattern-regex: "#pragma allow_unsafe_buffers"
23 |
--------------------------------------------------------------------------------
/assets/semgrep_rules/client/unsafejs-in-cpp.cc:
--------------------------------------------------------------------------------
1 | int main() {
2 | // ruleid: unsafe-js-in-cpp-strings
3 | const std::string kGetContentLength = "document.body.innerHTML.length";
4 |
5 | const std::string kGetStyleLength =
6 | // ruleid: unsafe-js-in-cpp-strings
7 | "document.getElementById('brave_speedreader_style').innerHTML.length";
8 | // ruleid: unsafe-js-in-cpp-strings
9 | const std::string altDot = "asd.document.write";
10 | }
--------------------------------------------------------------------------------
/assets/semgrep_rules/client/unsafejs-in-cpp.yaml:
--------------------------------------------------------------------------------
1 | rules:
2 | - id: unsafe-js-in-cpp-strings
3 | metadata:
4 | author: Andrea Brancaleoni
5 | confidence: LOW
6 | assignees: |
7 | diracdeltas
8 | thypon
9 | source: https://github.com/brave/security-action/blob/main/assets/semgrep_rules/client/unsafejs-in-cpp.yaml
10 | category: security
11 | message: Unsafe JS in CPP strings
12 | languages:
13 | - c
14 | - cpp
15 | paths:
16 | include:
17 | - "*.cpp"
18 | - "*.cc"
19 | - "*.c"
20 | - "*.h"
21 | - "*.hpp"
22 | - "*.hh"
23 | - "*.mm"
24 | exclude:
25 | - test/
26 | - "*.test.cc"
27 | - "*browsertest*.cc"
28 | - third_party/rust/*
29 | severity: WARNING
30 | patterns:
31 | - pattern-either:
32 | - pattern-regex: innerHTML
33 | - pattern-regex: document\.write
34 | - pattern-inside: |
35 | "..."
36 |
--------------------------------------------------------------------------------
/assets/semgrep_rules/client/web-contents-user-data.cc:
--------------------------------------------------------------------------------
1 | // ruleid: web-contents-user-data
2 | class MyTest : public WebContentsUserData {
3 | }
4 | // ruleid: web-contents-user-data
5 | class MyTest : public content::WebContentsUserData {
6 | }
7 | // ok: web-contents-user-data
8 | class MyTest : public WebContentsObserver {
9 | }
10 |
--------------------------------------------------------------------------------
/assets/semgrep_rules/client/web-contents-user-data.yaml:
--------------------------------------------------------------------------------
1 | rules:
2 | - id: web-contents-user-data
3 | metadata:
4 | author: Brian Johnson
5 | source: https://github.com/brave/security-action/blob/main/assets/semgrep_rules/client/web-contents-user-data.yaml
6 | assignees: |
7 | goodov
8 | cdesouza-chromium
9 | bridiver
10 | category: correctness
11 | references:
12 | - https://chromium.googlesource.com/chromium/src/+/main/docs/chrome_browser_design_principles.md#structure_modularity
13 | pattern-either:
14 | - pattern: public content::WebContentsUserData
15 | - pattern: public WebContentsUserData
16 | message: |
17 | Prefer dependency injection
18 |
19 | References:
20 | - https://chromium.googlesource.com/chromium/src/+/main/docs/chrome_browser_design_principles.md#structure_modularity
21 | languages:
22 | - generic
23 | paths:
24 | include:
25 | - "*.cc"
26 | - "*.h"
27 | severity: INFO
28 |
--------------------------------------------------------------------------------
/assets/semgrep_rules/client/web-ui-origin-encoding.ts:
--------------------------------------------------------------------------------
1 | function testWebUIOriginEncoding() {
2 | const param = "param=test param with spaces and URL https://example.com";
3 | const unencodedUrl = "https://example.com"
4 | const hash = "#section1";
5 | const path = "/path/to/resource";
6 |
7 | // ok: web-ui-origin-encoding
8 | const faviconUrl = "chrome://favicon";
9 |
10 | // ok: web-ui-origin-encoding
11 | const faviconUrl2 = "chrome://favicon/";
12 |
13 | // ok: web-ui-origin-encoding
14 | const faviconUrl3 = `chrome://favicon/size/64@1x/${unencodedUrl}`;
15 |
16 | // ok: web-ui-origin-encoding
17 | const staticUrl = "chrome://brave/settings";
18 |
19 | // ok: web-ui-origin-encoding
20 | const staticUrlWithHash = "chrome://brave/settings#general";
21 |
22 | // ok: web-ui-origin-encoding
23 | const goodPathUrl = `brave://wallet${path}`; //should ignore as it's a common approach for wallet routing
24 |
25 | // ok: web-ui-origin-encoding
26 | const goodUrl1 = `chrome://brave/${encodeURIComponent(param + param)}`;
27 |
28 | // ok: web-ui-origin-encoding
29 | const goodUrl2 = "chrome://brave/settings?" + encodeURIComponent(param) + encodeURIComponent(hash);
30 |
31 | // ok: web-ui-origin-encoding
32 | const goodUrl3 = `chrome://brave-rewards/${path}?param=${encodeURIComponent(param)}`;
33 |
34 | // ok: web-ui-origin-encoding
35 | const goodUrl4 = `chrome://brave-rewards` + path + `?param=${encodeURIComponent(param)}`;
36 |
37 | // ruleid: web-ui-origin-encoding
38 | const badFaviconUrl3 = `chrome://favicon2?url=${unencodedUrl}`; // favicon2 needs to be URI encoded still
39 |
40 | // ruleid: web-ui-origin-encoding
41 | const partialEncodingUrl = `chrome://brave/${encodeURIComponent(
42 | param
43 | )}${hash}`; // should still trigger as 'hash' isn't encoded
44 |
45 | // ruleid: web-ui-origin-encoding
46 | const multiParamEncoded = `chrome://brave/settings?param1=${encodeURIComponent(
47 | param
48 | )}¶m2=${encodeURIComponent(path)}`; // false positive, but too complex to handle this case so can just be manually checked
49 |
50 | // ruleid: web-ui-origin-encoding
51 | const badBraveWalletUrl = `brave://wallet/${param}`
52 |
53 | // ruleid: web-ui-origin-encoding
54 | const badUrl1 = `chrome://brave/${param}`; // should still throw because it's unencoded data
55 |
56 | // ruleid: web-ui-origin-encoding
57 | const badUrl2 = "chrome://brave/settings?" + param + hash;
58 |
59 | // ruleid: web-ui-origin-encoding
60 | const badUrl3 = `chrome://brave-rewards${path}?param=${param}`;
61 | }
62 |
--------------------------------------------------------------------------------
/assets/semgrep_rules/client/web-ui-origin-encoding.yaml:
--------------------------------------------------------------------------------
1 | rules:
2 | - id: web-ui-origin-encoding
3 | languages:
4 | - typescript
5 | severity: WARNING
6 | message: URI components should be encoded using encodeURIComponent. This
7 | prevents passed URLs and other encoded data from causing the WebUI URL
8 | from being malformed or parsed incorrectly.
9 | metadata:
10 | author: |
11 | Kyle Den Hartog
12 | Andrea Brancaleoni
13 | confidence: LOW
14 | assignees: |
15 | kdenhartog
16 | thypon
17 | source: https://github.com/brave/security-action/blob/main/assets/semgrep_rules/client/web-ui-origin-encoding.yaml
18 | category: security
19 | references:
20 | - https://github.com/brave/brave-browser/issues/43367
21 | paths:
22 | include:
23 | - "*.ts"
24 | - "*.js"
25 | - "*.tsx"
26 | exclude:
27 | - test/
28 | - "*.test.ts"
29 | patterns:
30 | - pattern-inside: '"..."' # get only strings
31 | - pattern-either:
32 | - pattern-regex: chrome:// # pattern should start with chrome:// to detect WebUI URLs
33 | - pattern-regex: brave://wallet # pattern should start with chrome:// to detect WebUI URLs
34 | - pattern-regex: .*(\$\{|\+).* # pattern should either contain a variable expansion with ${...} or adding a new string
35 | - pattern-not-regex: .*encodeURIComponent(...).* # pattern should not contain encodeURIComponent
36 | - pattern-not-regex: chrome://favicon/size/ # negate chrome://favicon legacy version which needs to be unencoded
37 | - pattern-not-regex: brave://wallet(\$\{)([A-Za-z0-9\/]+)\} # ignore cases where wallet routing via paths are in use
38 |
--------------------------------------------------------------------------------
/assets/semgrep_rules/generate-compound.rb:
--------------------------------------------------------------------------------
1 | require 'yaml'
2 |
3 | SEMGREP_VERSION = `semgrep --version`.strip
4 |
5 | HOST = 'https://semgrep.dev'
6 |
7 | files = Dir['client/*.yaml', 'services/*.yaml', 'frozen/*/vuln.yaml', 'frozen/*/audit.yml', 'generated/*/vulns.yaml', 'generated/*/audit.yaml']
8 |
9 | rules = {'rules' => []}
10 |
11 | files.each do |fname|
12 | begin
13 | irules = YAML.load(File.read(fname))['rules']
14 | puts "#{fname}: #{irules.length}"
15 |
16 | rules['rules'].concat irules
17 | rescue
18 | puts "Error in #{fname}"
19 | end
20 | end
21 |
22 | puts "#rules: #{rules['rules'].length}"
23 |
24 | File.write("compound.yaml", YAML.dump(rules))
25 |
26 | # require 'pry'
27 | # binding.pry
28 |
29 |
--------------------------------------------------------------------------------
/assets/semgrep_rules/services/activerecord-sanitize-sql-noop.rb:
--------------------------------------------------------------------------------
1 | # ruleid: activerecord-sanitize-sql-noop
2 | ActiveRecord::Base.sanitize_sql("#{channel}_channel_id")
3 |
4 | # ok: activerecord-sanitize-sql-noop
5 | sanitize_sql_for_conditions(["name=? and group_id=?", "foo'bar", 4])
6 | # => "name='foo''bar' and group_id=4"
7 |
8 | # ok: activerecord-sanitize-sql-noop
9 | sanitize_sql_for_conditions(["name=:name and group_id=:group_id", name: "foo'bar", group_id: 4])
10 | # => "name='foo''bar' and group_id='4'"
11 |
12 | # ok: activerecord-sanitize-sql-noop
13 | sanitize_sql_for_conditions(["name='%s' and group_id='%s'", "foo'bar", 4])
14 | # => "name='foo''bar' and group_id='4'"
15 |
16 | # ruleid: activerecord-sanitize-sql-noop
17 | sanitize_sql_for_conditions("#{user_generated}")
18 |
19 | # ruleid: activerecord-sanitize-sql-noop
20 | sanitize_sql_for_conditions(some_variable)
21 | # possibly a false-positive in the case that some_variable is the correct kind of hash
22 |
23 | # ok: activerecord-sanitize-sql-noop
24 | sanitize_sql_for_order([Arel.sql("field(id, ?)"), [1,3,2]])
25 | # => "field(id, 1,3,2)"
26 |
27 | # ruleid: activerecord-sanitize-sql-noop
28 | sanitize_sql_for_order("id ASC")
29 | # => "id ASC"
30 | # Yes, directly from the Rails documentation, and not dangerous as constant, but a no-op so bad
31 |
32 | # ruleid: activerecord-sanitize-sql-noop
33 | ActiveRecord::Base.sanitize_sql_for_order("#{order} ASC")
34 | # Like, you're just asking for errors to happen if you aren't whitelisting order anyway.
--------------------------------------------------------------------------------
/assets/semgrep_rules/services/activerecord-sanitize-sql-noop.yaml:
--------------------------------------------------------------------------------
1 | rules:
2 | - id: activerecord-sanitize-sql-noop
3 | patterns:
4 | - pattern-either:
5 | - pattern: ActiveRecord::Base.$FUNC($STR)
6 | - pattern: $FUNC($STR)
7 | - metavariable-regex:
8 | metavariable: $STR
9 | regex: "^[^[]"
10 | - metavariable-regex:
11 | metavariable: $FUNC
12 | regex: "^sanitize_sql(_for_(order|conditions))?$"
13 | message: |
14 | When $FUNC is called with a string argument rather than an array/hash, it returns the string as-is without sanitization.
15 | The method name is dangerously misleading.
16 | The method's intended use is to safely insert variables into a string containing '?' or ':param', producing a valid SQL fragment for use where parameterized queries will not work.
17 | This method will NOT sanitize just a SQL string.
18 | User input here is likely a SQL injection vulnerability.
19 | languages:
20 | - ruby
21 | severity: INFO
22 | metadata:
23 | author: Ben Caller
24 | references:
25 | - https://api.rubyonrails.org/classes/ActiveRecord/Sanitization/ClassMethods.html
26 | confidence: LOW
27 | source: https://github.com/brave/security-action/blob/main/assets/semgrep_rules/services/activerecord-sanitize-sql-noop.yaml
28 | category: security
29 |
--------------------------------------------------------------------------------
/assets/semgrep_rules/services/brave-third-party-action-not-pinned-to-commit-sha.test.yaml:
--------------------------------------------------------------------------------
1 | on:
2 | pull_request_target:
3 | pull_request:
4 | jobs:
5 | build:
6 | name: Build and test
7 | runs-on: ubuntu-latest
8 | steps:
9 | # ruleid: brave-third-party-action-not-pinned-to-commit-sha
10 | - uses: actions/checkout@v2 # v2.1.2
11 | with:
12 | ref: ${{ github.event.pull_request.head.sha }}
13 | # ruleid: brave-third-party-action-not-pinned-to-commit-sha
14 | - uses: actions/setup-node@master # v1.2.3
15 | - run: |
16 | npm install
17 | npm build
18 | # ok: brave-third-party-action-not-pinned-to-commit-sha
19 | - uses: ./.github/actions/do-a-local-action
20 | with:
21 | arg1: ${{ secrets.supersecret1 }}
22 | # ok: brave-third-party-action-not-pinned-to-commit-sha
23 | - uses: completely/fakeaction@5fd3084fc36e372ff1fff382a39b10d03659f355 # v1.2.3
24 | with:
25 | arg2: ${{ secrets.supersecret2 }}
26 | # ok: brave-third-party-action-not-pinned-to-commit-sha
27 | - uses: docker://alpine@sha256:402d21757a03a114d273bbe372fa4b9eca567e8b6c332fa7ebf982b902207242 # v1.2.3
28 | # ruleid: brave-third-party-action-not-pinned-to-commit-sha
29 | - uses: completely/fakeaction@5fd3084 # v1.2.3
30 | with:
31 | arg2: ${{ secrets.supersecret2 }}
32 | # ruleid: brave-third-party-action-not-pinned-to-commit-sha
33 | - uses: completely/fakeaction@5fd3084
34 | # ruleid: brave-third-party-action-not-pinned-to-commit-sha
35 | - uses: fakerepo/comment-on-pr@v1
36 | with:
37 | message: |
38 | Thank you!
39 | # ok: brave-third-party-action-not-pinned-to-commit-sha
40 | - uses: brave-intl/test@v1
41 | # ok: brave-third-party-action-not-pinned-to-commit-sha
42 | - uses: brave/test@v1
43 | # ok: brave-third-party-action-not-pinned-to-commit-sha
44 | - uses: brave-experiments/test@v1
45 | # ruleid: brave-third-party-action-not-pinned-to-commit-sha
46 | - uses: aws-actions/test@v1
47 | # ruleid: brave-third-party-action-not-pinned-to-commit-sha
48 | - uses: github/test@v1
49 | # ruleid: brave-third-party-action-not-pinned-to-commit-sha
50 | - uses: ruby/setup-ruby@v1
51 | # ruleid: brave-third-party-action-not-pinned-to-commit-sha
52 | - uses: slackapi/slack-github-action@v1.24.0
53 | # ruleid: brave-third-party-action-not-pinned-to-commit-sha
54 | - uses: fakerepo/comment-on-pr
55 | with:
56 | message: |
57 | Thank you!
58 | # ruleid: brave-third-party-action-not-pinned-to-commit-sha
59 | - uses: docker://gcr.io/cloud-builders/gradle
60 | # ruleid: brave-third-party-action-not-pinned-to-commit-sha
61 | - uses: docker://alpine:3.8
62 | - name: Notify Slack of success
63 | # ruleid: brave-third-party-action-not-pinned-to-commit-sha
64 | uses: 8398a7/action-slack@28ba43ae48961b90635b50953d216767a6bea486
65 |
66 | build2:
67 | name: Build and test using a local workflow
68 | # ok: brave-third-party-action-not-pinned-to-commit-sha
69 | uses: ./.github/workflows/use_a_local_workflow.yml@master
70 | secrets: inherit
71 | with:
72 | examplearg: true
73 |
--------------------------------------------------------------------------------
/assets/semgrep_rules/services/brave-third-party-action-not-pinned-to-commit-sha.yaml:
--------------------------------------------------------------------------------
1 | rules:
2 | - id: brave-third-party-action-not-pinned-to-commit-sha
3 | languages:
4 | - yaml
5 | severity: ERROR
6 | message: |
7 | An action sourced from a third-party repository on GitHub is not pinned to a full length commit SHA or is missing the semver reference comment
8 |
9 | You can use pinact - https://github.com/suzuki-shunsuke/pinact - to pin them
10 |
11 | 👍
12 |
13 | `uses: actions/cache@88522ab9f39a2ea568f7027eddc7d8d8bc9d59c8 # v3.3.1`
14 |
15 | 👎
16 |
17 | `uses: actions/cache@v3`
18 | `uses: actions/cache@v3.3.1`
19 |
20 | [GHA Policies](https://github.com/brave/internal/wiki/Pull-request-security-audit-checklist)
21 | patterns:
22 | - pattern-regex: "uses:\\s+(?.*)\\s*$"
23 | - metavariable-pattern:
24 | metavariable: $USES
25 | language: generic
26 | patterns:
27 | - pattern-not-regex: ^[.]/
28 | - pattern-not-regex: ^brave/
29 | - pattern-not-regex: ^brave-intl/
30 | - pattern-not-regex: ^brave-experiments/
31 | - pattern-not-regex: "@[0-9a-f]{40}\\s+#\\s+v?\\d+\\.\\d+\\.\\d+$"
32 | - pattern-not-regex: "^docker://.*@sha256:[0-9a-f]{64}\\s+#\\s+v?\\d+\\.\\d+\\.\\d+$"
33 | metadata:
34 | cwe:
35 | - "CWE-1357: Reliance on Insufficiently Trustworthy Component"
36 | owasp: A06:2021 - Vulnerable and Outdated Components
37 | references:
38 | - https://owasp.org/Top10/A06_2021-Vulnerable_and_Outdated_Components
39 | - https://docs.github.com/en/actions/security-guides/security-hardening-for-github-actions#using-third-party-actions
40 | source: https://github.com/brave/security-action/blob/main/assets/semgrep_rules/services/brave-third-party-action-not-pinned-to-commit-sha.yaml
41 | category: security
42 | technology:
43 | - github-actions
44 | subcategory:
45 | - vuln
46 | likelihood: LOW
47 | impact: LOW
48 | confidence: HIGH
49 | license: Commons Clause License Condition v1.0[LGPL-2.1-only]
50 | vulnerability_class:
51 | - Other
52 |
--------------------------------------------------------------------------------
/assets/semgrep_rules/services/detected-aws-access-key-id-value.js:
--------------------------------------------------------------------------------
1 | // ruleid: detected-aws-access-key-id-value
2 | var AWS_ACCESS_KEY_ID = "AKIABBBBBBBBBBBBBB1B";
3 |
4 | // ruleid: detected-aws-access-key-id-value
5 | /*
6 | AKIABBBBBBBBBBBBBB1B
7 | */
8 |
9 | // ok: detected-aws-access-key-id-value
10 | const SOUVLAKIAISTOTALLYDELICIOUS = true;
--------------------------------------------------------------------------------
/assets/semgrep_rules/services/detected-aws-access-key-id-value.yaml:
--------------------------------------------------------------------------------
1 | rules:
2 | - id: detected-aws-access-key-id-value
3 | patterns:
4 | - pattern-regex: (^|[^A-Za-z0-9])(A3T[A-Z0-9]|AKIA|AGPA|AROA|AIPA|ANPA|ANVA|ASIA)[A-Z0-9]{16}
5 | - pattern-not-regex: (?i)example|sample|test|fake
6 | languages:
7 | - regex
8 | message: AWS Access Key ID Value detected. This is a sensitive credential and should not be hardcoded here. Instead, read this value from an environment variable or keep it in a separate, private file.
9 | severity: ERROR
10 | metadata:
11 | cwe:
12 | - "CWE-798: Use of Hard-coded Credentials"
13 | source-rule-url: https://github.com/grab/secret-scanner/blob/master/scanner/signatures/pattern.go
14 | category: security
15 | technology:
16 | - secrets
17 | - aws
18 | confidence: LOW
19 | owasp:
20 | - A07:2021 - Identification and Authentication Failures
21 | references:
22 | - https://owasp.org/Top10/A07_2021-Identification_and_Authentication_Failures
23 | - https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_identifiers.html#identifiers-prefixes
24 | - https://semgrep.dev/r?q=generic.secrets.security.detected-aws-access-key-id-value.detected-aws-access-key-id-value
25 | cwe2022-top25: true
26 | cwe2021-top25: true
27 | subcategory:
28 | - audit
29 | likelihood: LOW
30 | impact: HIGH
31 | license: Commons Clause License Condition v1.0[LGPL-2.1-only]
32 | source: https://github.com/brave/security-action/blob/main/assets/semgrep_rules/services/detected-aws-access-key-id-value.yaml
33 |
--------------------------------------------------------------------------------
/assets/semgrep_rules/services/docker-compose-no-new-privileges.test.yaml:
--------------------------------------------------------------------------------
1 | version: '3'
2 | name: mytest
3 | services:
4 | # Ignore when extended (hopefully the base file is secure :/)
5 | # ok: docker-compose-no-new-privileges
6 | ser:
7 | e: f
8 | extends:
9 | file: docker-compose-common.yml
10 | service: lts
11 | g: h
12 | image: links
13 | i: j
14 | # ruleid: docker-compose-no-new-privileges
15 | bad:
16 | a: b
17 | image: links
18 | c: d
19 | # Ignore when extended (hopefully the base file is secure :/)
20 | # ok: docker-compose-no-new-privileges
21 | vice:
22 | x: y
23 | extends:
24 | file: ../../docker-compose.yml
25 | service: vice
26 | volumes:
27 | - /x:/y
28 |
--------------------------------------------------------------------------------
/assets/semgrep_rules/services/docker-compose-no-new-privileges.yaml:
--------------------------------------------------------------------------------
1 | rules:
2 | - id: docker-compose-no-new-privileges
3 | patterns:
4 | - pattern-inside: |
5 | version: ...
6 | ...
7 | services:
8 | ...
9 | - pattern: |
10 | $SERVICE:
11 | ...
12 | image: ...
13 | - pattern-not: |
14 | $SERVICE:
15 | ...
16 | image: ...
17 | ...
18 | security_opt:
19 | - ...
20 | - no-new-privileges=true
21 | - ...
22 | - pattern-not: |
23 | $SERVICE:
24 | ...
25 | extends: ...
26 | - focus-metavariable: "$SERVICE"
27 | message: Service '$SERVICE' allows for privilege escalation via setuid or setgid binaries. Add 'no-new-privileges=true' in 'security_opt' to prevent this.
28 | metadata:
29 | cwe:
30 | - 'CWE-732: Incorrect Permission Assignment for Critical Resource'
31 | owasp:
32 | - A05:2021 - Security Misconfiguration
33 | - A06:2017 - Security Misconfiguration
34 | references:
35 | - https://docs.docker.com/engine/reference/run/#security-configuration
36 | - https://raesene.github.io/blog/2019/06/01/docker-capabilities-and-no-new-privs/
37 | - https://www.kernel.org/doc/Documentation/prctl/no_new_privs.txt
38 | - https://cheatsheetseries.owasp.org/cheatsheets/Docker_Security_Cheat_Sheet.html#rule-4-add-no-new-privileges-flag
39 | category: security
40 | technology:
41 | - docker-compose
42 | cwe2021-top25: true
43 | subcategory:
44 | - audit
45 | likelihood: LOW
46 | impact: HIGH
47 | confidence: LOW
48 | license: Commons Clause License Condition v1.0[LGPL-2.1-only]
49 | vulnerability_class:
50 | - Improper Authorization
51 | source-rule-url: https://semgrep.dev/r/yaml.docker-compose.security.no-new-privileges.no-new-privileges
52 | source: https://github.com/brave/security-action/blob/main/assets/semgrep_rules/services/docker-compose-no-new-privileges.yaml
53 | languages:
54 | - yaml
55 | severity: WARNING
56 |
--------------------------------------------------------------------------------
/assets/semgrep_rules/services/find-links-without-no-index.txt:
--------------------------------------------------------------------------------
1 | sphinx-tabs == 3.4.1
2 | sphinx-rtd-theme
3 | sphinx == 5.2.3
4 | sphinx-toolbox == 3.4.0
5 | tlcpack-sphinx-addon==0.2.2
6 | sphinxcontrib_httpdomain==1.8.1
7 | sphinxcontrib-napoleon==0.7
8 | sphinx-reredirects==0.1.2
9 | // ruleid: find-links-without-no-index
10 | --find-links https://mlc.ai/wheels rw
11 | mlc-ai-nightly
12 | --find-links --no-index https://mlc.ai/wheels
13 | --no-index --find-links https://mlc.ai/wheels
14 | // ruleid: find-links-without-no-index
15 | pip3 install --quiet --pre -U -f https://mlc.ai/wheels mlc-ai-nightly
16 | // ruleid: find-links-without-no-index
17 | pip install --quiet --pre -U -f https://mlc.ai/wheels mlc-ai-nightly
18 | pip install --quiet --pre -U -f --no-index https://mlc.ai/wheels mlc-ai-nightly
19 |
--------------------------------------------------------------------------------
/assets/semgrep_rules/services/find-links-without-no-index.yaml:
--------------------------------------------------------------------------------
1 | rules:
2 | - id: find-links-without-no-index
3 | metadata:
4 | author: Artem Chaikin
5 | source: https://github.com/brave/security-action/blob/main/assets/semgrep_rules/services/find-links-without-no-index.yaml
6 | references:
7 | - https://portswigger.net/daily-swig/dependency-confusion-attack-mounted-via-pypi-repo-exposes-flawed-package-installer-behavior
8 | - https://www.bleepingcomputer.com/news/security/pytorch-discloses-malicious-dependency-chain-compromise-over-holidays/
9 | confidence: LOW
10 | category: security
11 | pattern-either:
12 | - pattern-regex: ^(?!.*--no-index).*--find-links
13 | - pattern-regex: ^(?!.*--no-index).*(pip|pip3)\s.*\s-f
14 | message: "When --find-links or -f is used without --no-index, pip may try to install the package from PyPI. Add --no-index to avoid dependency confusion."
15 | severity: INFO
16 | languages:
17 | - generic
18 |
--------------------------------------------------------------------------------
/assets/semgrep_rules/services/http-parse-multipart-dos.go:
--------------------------------------------------------------------------------
1 | func handleBad(w http.ResponseWriter, r *http.Request) error {
2 | // ruleid: http-parse-multipart-dos
3 | if err = r.ParseMultipartForm(maxSize); err != nil {
4 | return err
5 | }
6 | return nil
7 | }
8 |
9 | func handleOK(w http.ResponseWriter, r *http.Request) error {
10 | r.Body = http.MaxBytesReader(w, r.Body, 123)
11 | fmt.Print("banana")
12 | // ok: http-parse-multipart-dos
13 | if err = r.ParseMultipartForm(maxSize); err != nil {
14 | return err
15 | }
16 | return nil
17 | }
--------------------------------------------------------------------------------
/assets/semgrep_rules/services/http-parse-multipart-dos.yaml:
--------------------------------------------------------------------------------
1 | rules:
2 | - id: http-parse-multipart-dos
3 | metadata:
4 | author: Ben Caller
5 | confidence: LOW
6 | references:
7 | - https://pkg.go.dev/net/http#Request.ParseMultipartForm
8 | - https://pkg.go.dev/net/http#MaxBytesReader
9 | source: https://github.com/brave/security-action/blob/main/assets/semgrep_rules/services/http-parse-multipart-dos.yaml
10 | assignees: |
11 | thypon
12 | kdenhartog
13 | category: security
14 | severity: INFO
15 | languages:
16 | - go
17 | patterns:
18 | - pattern: $R.ParseMultipartForm($MAXSIZE)
19 | - pattern-not-inside: |
20 | $R.Body = http.MaxBytesReader($W, <...$R.Body...>, $LIMIT)
21 | ...
22 | fix: $R.Body = http.MaxBytesReader($W, $R.Body, $MAXSIZE)
23 | message: |-
24 | ParseMultipartForm is vulnerable to Denial of Service (DoS) by clients sending a large HTTP request body.
25 | The specified limit of $MAXSIZE is the maximum amount stored in memory.
26 | The remainder is still parsed and stored on disk in temporary files.
27 | Wrapping $R.Body with http.MaxBytesReader prevents this wasting of server resources.
28 |
--------------------------------------------------------------------------------
/assets/semgrep_rules/services/insecure-types.go:
--------------------------------------------------------------------------------
1 | // LICENSE: Commons Clause License Condition v1.0[LGPL-2.1-only]
2 | // original source: https://github.com/returntocorp/semgrep-rules/blob/5b098c252feec688d243cef046d07597a546c25b/go/template/security/insecure-types.go
3 |
4 | package main
5 |
6 | import "fmt"
7 | import "html/template"
8 |
9 | func main() {
10 | var g = "foo"
11 |
12 | // ruleid:go-insecure-templates
13 | const a template.HTML = fmt.Sprintf("link")
14 | // ruleid:go-insecure-templates
15 | var b template.CSS = "a { text-decoration: underline; } "
16 |
17 | // ruleid:go-insecure-templates
18 | var c template.HTMLAttr = fmt.Sprintf("herf=%q")
19 |
20 | // ruleid:go-insecure-templates
21 | const d template.JS = "{foo: 'bar'}"
22 |
23 | // ruleid:go-insecure-templates
24 | var e template.JSStr = "setTimeout('alert()')";
25 |
26 | // ruleid:go-insecure-templates
27 | var f template.Srcset = g;
28 |
29 | // ok:go-insecure-templates
30 | tmpl, err := template.New("test").ParseFiles("file.txt")
31 |
32 | // other code
33 | myTpl.Execute(w, a);
34 | }
35 |
--------------------------------------------------------------------------------
/assets/semgrep_rules/services/insecure-types.yaml:
--------------------------------------------------------------------------------
1 | rules:
2 | - id: go-insecure-templates
3 | patterns:
4 | - pattern-inside: |
5 | import "html/template"
6 | ...
7 | - pattern-either:
8 | - pattern: var $VAR template.HTML = $EXP
9 | - pattern: var $VAR template.CSS = $EXP
10 | - pattern: var $VAR template.HTMLAttr = $EXP
11 | - pattern: var $VAR template.JS = $EXP
12 | - pattern: var $VAR template.JSStr = $EXP
13 | - pattern: var $VAR template.Srcset = $EXP
14 | message: >-
15 | usage of insecure template types. They are documented as a security risk. See https://golang.org/pkg/html/template/#HTML.
16 | severity: WARNING
17 | metadata:
18 | source: https://github.com/brave/security-action/blob/main/assets/semgrep_rules/services/insecure-types.yaml
19 | original_source: https://github.com/returntocorp/semgrep-rules/blob/5b098c252feec688d243cef046d07597a546c25b/go/template/security/insecure-types.yaml
20 | cwe:
21 | - "CWE-79: Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')"
22 | references:
23 | - https://golang.org/pkg/html/template/#HTML
24 | - https://twitter.com/empijei/status/1275177219011350528
25 | category: security
26 | technology:
27 | - template
28 | confidence: LOW
29 | owasp:
30 | - A07:2017 - Cross-Site Scripting (XSS)
31 | - A03:2021 - Injection
32 | cwe2022-top25: true
33 | cwe2021-top25: true
34 | subcategory:
35 | - audit
36 | likelihood: LOW
37 | impact: MEDIUM
38 | license: Commons Clause License Condition v1.0[LGPL-2.1-only]
39 | languages:
40 | - go
41 |
--------------------------------------------------------------------------------
/assets/semgrep_rules/services/internal-digest-call.py:
--------------------------------------------------------------------------------
1 | def signature(**kwargs):
2 | # ruleid: internal-digest-call
3 | sig = _INTERNAL_DIGEST_NEVER_CALL_DIRECTLY(kwargs)
4 |
--------------------------------------------------------------------------------
/assets/semgrep_rules/services/internal-digest-call.yaml:
--------------------------------------------------------------------------------
1 | rules:
2 | - id: internal-digest-call
3 | pattern-regex: _INTERNAL_DIGEST_NEVER_CALL_DIRECTLY
4 | message: Internal Digest Direct Call, never call this directly
5 | languages:
6 | - python
7 | severity: WARNING
8 | metadata:
9 | source: https://github.com/brave/security-action/blob/main/assets/semgrep_rules/services/internal-digest-call.yaml
10 | assignees: |
11 | stoletheminerals
12 | cdesouza-chromium
13 | bridiver
14 |
--------------------------------------------------------------------------------
/assets/semgrep_rules/services/io-readall-dos.go:
--------------------------------------------------------------------------------
1 | func handleBad(w http.ResponseWriter, r *http.Request) []byte {
2 | // ruleid: io-readall-dos
3 | payload, _ = io.ReadAll(r.Body)
4 | return payload
5 | }
6 |
7 | func handleOK(w http.ResponseWriter, r *http.Request) []byte {
8 | r.Body = http.MaxBytesReader(w, r.Body, 123)
9 | fmt.Print("banana")
10 | // ok: io-readall-dos
11 | payload, _ = io.ReadAll(r.Body)
12 | return payload
13 | }
14 |
15 | // ok: io-readall-dos
16 | io.ReadAll(io.LimitReader(r.Body, u.maxRequestSize))
17 |
18 | // ok: io-readall-dos
19 | io.ReadAll(x.y)
--------------------------------------------------------------------------------
/assets/semgrep_rules/services/io-readall-dos.yaml:
--------------------------------------------------------------------------------
1 | rules:
2 | - id: io-readall-dos
3 | metadata:
4 | author: Ben Caller
5 | confidence: LOW
6 | references:
7 | - https://pkg.go.dev/io#ReadAll
8 | - https://pkg.go.dev/net/http#MaxBytesReader
9 | source: https://github.com/brave/security-action/blob/main/assets/semgrep_rules/services/io-readall-dos.yaml
10 | assignees: |
11 | thypon
12 | kdenhartog
13 | category: security
14 | severity: INFO
15 | languages:
16 | - go
17 | patterns:
18 | - pattern: io.ReadAll($R.Body)
19 | - pattern-not-inside: |
20 | $R.Body = http.MaxBytesReader($W, <...$R.Body...>, $LIMIT)
21 | ...
22 | fix: io.ReadAll(http.MaxBytesReader(w, $R.Body, MAX_REQUEST_SIZE))
23 | message: |-
24 | io.ReadAll is vulnerable to Denial of Service (DoS) by clients sending a large HTTP request body.
25 | Wrapping $R.Body with http.MaxBytesReader (or io.LimitReader) prevents this wasting of server resources.
26 |
--------------------------------------------------------------------------------
/assets/semgrep_rules/services/jinja-safe-usages.html:
--------------------------------------------------------------------------------
1 | {% extends "layout.html" %}
2 |
3 | {% block body %}
4 | {{article.title}}
5 | Written by {{article.author}} on {{article.create_date}}
6 |
7 |
8 |
9 | {{article.body | safe}}
10 |
11 | {{article.body | safe }}
12 |
13 | {{article.head | clean | safe}}
14 |
15 | {{article.head|clean|safe}}
16 |
17 | {{article.body | safeBlah}}
18 |
19 | {% endblock %}
--------------------------------------------------------------------------------
/assets/semgrep_rules/services/jinja-safe-usages.yaml:
--------------------------------------------------------------------------------
1 | rules:
2 | - id: template-unescaped-with-safe-noclean
3 | message: Detected a segment of a Flask template where autoescaping is explicitly disabled with '| safe' filter. This allows rendering of raw HTML in this segment. Ensure no user data is rendered here, otherwise this is a cross-site scripting (XSS) vulnerability.
4 | metadata:
5 | cwe:
6 | - "CWE-79: Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')"
7 | owasp:
8 | - A07:2017 - Cross-Site Scripting (XSS)
9 | - A03:2021 - Injection
10 | references:
11 | - https://flask.palletsprojects.com/en/1.1.x/security/#cross-site-scripting-xss
12 | category: security
13 | technology:
14 | - flask
15 | cwe2022-top25: true
16 | cwe2021-top25: true
17 | subcategory:
18 | - audit
19 | likelihood: LOW
20 | impact: MEDIUM
21 | confidence: LOW
22 | license: Commons Clause License Condition v1.0[LGPL-2.1-only]
23 | source: https://github.com/brave/security-action/blob/main/assets/semgrep_rules/services/jinja-safe-usages.yaml
24 | languages:
25 | - regex
26 | paths:
27 | include:
28 | - "*.html"
29 | severity: WARNING
30 | patterns:
31 | - pattern-regex: "{{.*?\\|\\s*safe(\\s*}})?"
32 | - pattern-not-regex: "{{.*?clean\\s*\\|\\s*safe(\\s*}})?"
33 | - pattern-not-regex: "{{.*?\\|\\s*safe[^\\s}]"
34 |
--------------------------------------------------------------------------------
/assets/semgrep_rules/services/missing-integrity.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Document
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/assets/semgrep_rules/services/missing-integrity.yaml:
--------------------------------------------------------------------------------
1 | rules:
2 | - id: missing-integrity
3 | metadata:
4 | category: security
5 | technology:
6 | - html
7 | cwe:
8 | - 'CWE-353: Missing Support for Integrity Check'
9 | owasp:
10 | - A08:2021 - Software and Data Integrity Failures
11 | confidence: LOW
12 | references:
13 | - https://owasp.org/Top10/A08_2021-Software_and_Data_Integrity_Failures
14 | subcategory:
15 | - audit
16 | likelihood: LOW
17 | impact: LOW
18 | source: https://github.com/brave/security-action/blob/main/assets/semgrep_rules/services/missing-integrity.yaml
19 | patterns:
20 | - pattern-either:
21 | - pattern:
22 | - pattern:
23 | - metavariable-pattern:
24 | metavariable: $...A
25 | patterns:
26 | - pattern-either:
27 | - pattern: src='... :// ...'
28 | - pattern: src="... :// ..."
29 | - pattern: href='... :// ...'
30 | - pattern: href="... :// ..."
31 | - pattern: src='//...'
32 | - pattern: src="//..."
33 | - pattern: href='//...'
34 | - pattern: href="//..."
35 | - pattern-not-regex: (?is).*integrity=
36 | - pattern-not-regex: (google-analytics.com|fonts.googleapis.com|googletagmanager.com)
37 | - pattern-not-regex: 'chrome:\/\/'
38 | - pattern-not-regex: 'chrome-untrusted:\/\/'
39 | paths:
40 | include:
41 | - '*.html'
42 | message: >-
43 | This tag is missing an 'integrity' subresource integrity attribute. The 'integrity' attribute allows for the browser to verify that externally hosted files (for example from a CDN) are delivered without unexpected manipulation. Without this attribute, if an attacker can modify the externally hosted resource, this could lead to XSS and other types of attacks. To prevent this, include the base64-encoded cryptographic hash of the resource (file) you’re telling the browser to fetch in the 'integrity' attribute for all externally hosted files.
44 | severity: WARNING
45 | languages: [generic]
46 |
--------------------------------------------------------------------------------
/assets/semgrep_rules/services/missing-noopener-window-open-native.js:
--------------------------------------------------------------------------------
1 | // ruleid: missing-noopener-window-open-native
2 | window.open("something")
3 | // ruleid: missing-noopener-window-open-native
4 | window.open("ciao", "biao")
5 | // ruleid: missing-noopener-window-open-native
6 | open("ciao", "ciao")
7 |
8 | // ok: missing-noopener-window-open-native
9 | window.open("ciao", "bao", "noopeners", "asd")
10 | // ruleid: missing-noopener-window-open-native
11 | window.open("ciao", "bao", "antani", "asd")
12 |
--------------------------------------------------------------------------------
/assets/semgrep_rules/services/missing-noopener-window-open-native.yaml:
--------------------------------------------------------------------------------
1 | rules:
2 | - id: missing-noopener-window-open-native
3 | message: window.open should enforce `noopener` to avoid tab-nabbing vulnerabilities.
4 | metadata:
5 | author: Andrea Brancaleoni @ Brave
6 | confidence: LOW
7 | cwe:
8 | - "CWE-200: Exposure of Sensitive Information to an Unauthorized Actor"
9 | owasp:
10 | - A01:2021 - Broken Access Control
11 | references:
12 | - https://web.dev/external-anchors-use-rel-noopener/
13 | - https://html.spec.whatwg.org/multipage/links.html#link-type-noreferrer
14 | category: security
15 | cwe2021-top25: true
16 | subcategory:
17 | - audit
18 | likelihood: LOW
19 | impact: LOW
20 | license: MIT
21 | source: https://github.com/brave/security-action/blob/main/assets/semgrep_rules/services/missing-noopener-window-open-native.yaml
22 | languages:
23 | - typescript
24 | - javascript
25 | paths:
26 | exclude:
27 | - '*test*'
28 | severity: INFO
29 | patterns:
30 | - pattern-either:
31 | - patterns:
32 | - pattern-either:
33 | - pattern: window.open($...URL)
34 | - pattern: document.open($...URL)
35 | - pattern: open($...URL)
36 | - metavariable-comparison:
37 | metavariable: $...URL
38 | comparison: not re.match('.*(chrome|brave)(-untrusted)?://.*', str($...URL)) and re.match('^([^,]*|[^,]*,[^,]*)$', str($...URL))
39 | - patterns:
40 | - pattern-either:
41 | - pattern: window.open($URL, $TARGET, $FEATURES, ...)
42 | - pattern: document.open($URL, $TARGET, $FEATURES, ...)
43 | - pattern: open($URL, $TARGET, $FEATURES, ...)
44 | - metavariable-comparison:
45 | metavariable: $...FEATURES
46 | comparison: not re.match(".*no(opener|referrer).*", str($FEATURES))
47 |
--------------------------------------------------------------------------------
/assets/semgrep_rules/services/missing-noopener-window-open.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/assets/semgrep_rules/services/no-backticks-in-js-handlers.html:
--------------------------------------------------------------------------------
1 | // ruleid: no-backticks-in-js-handlers
2 | onclick="call('good', `{{var}}`, `{{var}}`)"
3 | // ruleid: no-backticks-in-js-handlers
4 | onclick='call("good", `{{var}}`, `{{var}}`)'
5 | // ruleid: no-backticks-in-js-handlers
6 | onclick=call('good', `{{var}}`, `{{var}}`)
7 |
--------------------------------------------------------------------------------
/assets/semgrep_rules/services/no-backticks-in-js-handlers.yaml:
--------------------------------------------------------------------------------
1 | rules:
2 | - id: no-backticks-in-js-handlers
3 | metadata:
4 | author: Andrea Brancaleoni @ Brave
5 | source: https://github.com/brave/security-action/blob/main/assets/semgrep_rules/services/no-backticks-in-js-handlers.yaml
6 | category: security
7 | patterns:
8 | - pattern-either:
9 | - pattern-inside: $HANDLER="..."
10 | - pattern-inside: $HANDLER='...'
11 | - pattern-inside: $HANDLER=...
12 | - pattern-regex: '`{{[^}]+}}`'
13 | - metavariable-regex:
14 | metavariable: $HANDLER
15 | regex: (?i)on[a-z]{3,40}
16 | message: |
17 | Backtick in JS handler may cause XSS since they are typically not auto-escaped in variables.
18 |
19 | Consider using single or double quotes instead of backticks.
20 | languages: [generic]
21 | severity: WARNING
22 |
--------------------------------------------------------------------------------
/assets/semgrep_rules/services/nodejs-insecure-url-parse.js:
--------------------------------------------------------------------------------
1 | // ruleid: nodejs-insecure-url-parse
2 | url.parse("here lies dragons");
3 | // ruleid: nodejs-insecure-url-parse
4 | require('url').parse("here lies dragons");
5 |
6 | var uparser = require('url');
7 |
8 | // ruleid: nodejs-insecure-url-parse
9 | uparser.parse("here lies dragons");
10 |
11 | function() {
12 | // ruleid: nodejs-insecure-url-parse
13 | uparser.parse("here lies dragons");
14 | }
15 |
16 | // ruleid: nodejs-insecure-url-parse
17 | setTimeout(()=> uparser.parse("here lies dragons"), 1000);
18 |
--------------------------------------------------------------------------------
/assets/semgrep_rules/services/nodejs-insecure-url-parse.yaml:
--------------------------------------------------------------------------------
1 | rules:
2 | - id: nodejs-insecure-url-parse
3 | metadata:
4 | author: Andrea Brancaleoni
5 | source: https://github.com/brave/security-action/blob/main/assets/semgrep_rules/services/nodejs-insecure-url-parse.yaml
6 | assignees: |
7 | thypon
8 | fmarier
9 | references:
10 | - https://nodejs.org/api/url.html#urlparseurlstring-parsequerystring-slashesdenotehost
11 | - https://nodejs.org/api/url.html#the-whatwg-url-api
12 | category: security
13 | pattern-either:
14 | - pattern: url.parse(...)
15 | - pattern: require('url').parse(...)
16 | message: Avoid using url.parse() as it is prone to security issues such as hostname spoofing. Use the URL class instead.
17 | severity: ERROR
18 | languages:
19 | - javascript
20 |
--------------------------------------------------------------------------------
/assets/semgrep_rules/services/not-using-hasownproperty-proto-access.js:
--------------------------------------------------------------------------------
1 | export function getThing(thing) {
2 | // ruleid: not-using-hasownproperty-proto-access
3 | if (thing in thingContainer) {
4 | return thingContainer[thing];
5 | }
6 | }
--------------------------------------------------------------------------------
/assets/semgrep_rules/services/not-using-hasownproperty-proto-access.yaml:
--------------------------------------------------------------------------------
1 | rules:
2 | - id: not-using-hasownproperty-proto-access
3 | pattern: |
4 | if ($X in $OBJ) {
5 | ...
6 | $OBJ[$X]
7 | ...
8 | }
9 | message: This allows $X to be `__proto__` or `toString`. To prevent prototype access use Object.prototype.hasOwnProperty.call($OBJ, $X) or use a Map.
10 | languages:
11 | - js
12 | - ts
13 | severity: WARNING
14 | metadata:
15 | category: security
16 | references:
17 | - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/hasOwnProperty
18 | source: https://github.com/brave/security-action/blob/main/assets/semgrep_rules/services/not-using-hasownproperty-proto-access.yaml
19 |
--------------------------------------------------------------------------------
/assets/semgrep_rules/services/path-travesal-by-string-interpolation.ts:
--------------------------------------------------------------------------------
1 | async function x() {
2 | // ruleid: path-travesal-by-string-interpolation
3 | await fetch(`${env.API_HOSTNAME}/api/subscriptions/${subscriptionId}`, 1337);
4 | // ruleid: path-travesal-by-string-interpolation
5 | await fetch(`${env.API_HOSTNAME}/api/subscriptions/${subscriptionId}`);
6 | // ok: path-travesal-by-string-interpolation
7 | await fetch(`${env.API_HOSTNAME}/api/subscriptions/?s=${subscriptionId}`, 123);
8 | }
--------------------------------------------------------------------------------
/assets/semgrep_rules/services/path-travesal-by-string-interpolation.yaml:
--------------------------------------------------------------------------------
1 | rules:
2 | - id: path-travesal-by-string-interpolation
3 | metadata:
4 | author: Ben Caller
5 | confidence: MEDIUM
6 | source: https://github.com/brave/security-action/blob/main/assets/semgrep_rules/services/path-travesal-by-string-interpolation.yaml
7 | category: security
8 | message: The code contains new security hotspots which should be checked manually by a security team member! Could a user perform path traversal by setting a variable to include `../`?
9 | severity: INFO
10 | languages:
11 | - ts
12 | - js
13 | paths:
14 | include:
15 | - "*.server.ts"
16 | - "*.server.js"
17 | - path-travesal-by-string-interpolation.ts
18 | - path-travesal-by-string-interpolation.svelte
19 | patterns:
20 | - pattern: fetch($URL, ...)
21 | - metavariable-regex:
22 | metavariable: $URL
23 | # Trigger on /x/${v} but don't trigger on /x/?y=${v}
24 | regex: (`.[^#?]+\$)
25 |
--------------------------------------------------------------------------------
/assets/semgrep_rules/services/pip-extra-index-url.Makefile:
--------------------------------------------------------------------------------
1 | venv/activate/test:
2 | $(info Creating virtual environment for test at ${VENV_PATH})
3 | python3.11 -m venv $(VENV_PATH)
4 | $(VENV_PIP) install --upgrade pip wheel setuptools
5 | # ruleid: pip-extra-index-url
6 | $(VENV_PIP) install --extra-index-url ${PYPI_INDEX_URL} --trusted-host ${PYPI_TRUSTED_HOST} my-very-private-package
7 | $(VENV_PIP) install --editable ${WHATEVER_PATH}/../../shared/horse/
--------------------------------------------------------------------------------
/assets/semgrep_rules/services/pip-extra-index-url.yaml:
--------------------------------------------------------------------------------
1 | rules:
2 | - id: pip-extra-index-url
3 | metadata:
4 | author: Ben Caller
5 | references:
6 | - https://portswigger.net/daily-swig/dependency-confusion-attack-mounted-via-pypi-repo-exposes-flawed-package-installer-behavior
7 | - https://www.bleepingcomputer.com/news/security/pytorch-discloses-malicious-dependency-chain-compromise-over-holidays/
8 | confidence: LOW
9 | source: https://github.com/brave/security-action/blob/main/assets/semgrep_rules/services/pip-extra-index-url.yaml
10 | category: security
11 | message: >-
12 | Use --index-url instead of --extra-index-url to avoid dependency confusion. When using --extra-index-url, pip looks on pypi.org as well as the private index. It may install a malicious package from pypi.org with the same name as your private package instead of the package in your private index.
13 | severity: INFO
14 | languages:
15 | - generic
16 | pattern: --extra-index-url
17 |
--------------------------------------------------------------------------------
/assets/semgrep_rules/services/react-href-var.tsx:
--------------------------------------------------------------------------------
1 | // ok: react-href-var
2 | let zzz = ;
3 |
4 | function test1(input) {
5 | // ruleid: react-href-var
6 | const params = {href: input.a};
7 | return React.createElement("a", params);
8 | }
9 |
10 | // ok: react-href-var
11 | {collaborationSectionData.paragraphs.map((item, i) => (
12 |
13 | ))}
14 |
15 | // ok: react-href-var
16 | let zzz = ;
17 |
18 | // ok: react-href-var
19 | let zzz = ;
20 |
21 | // ok: react-href-var
22 | let zzz = ;
23 |
24 | function test1(input) {
25 | // ok: react-href-var
26 | if(input.startsWith("https:")) {
27 | const params = {href: input};
28 | return React.createElement("a", params);
29 | }
30 | }
31 |
32 | function test2(input) {
33 | // ok: react-href-var
34 | const params = {href: "#"+input};
35 | return React.createElement("a", params);
36 | }
37 |
38 | function test2(input) {
39 | // ok: react-href-var
40 | const params = {href: "#"+input};
41 | return React.createElement("a", params);
42 | }
43 |
44 |
45 | // ok: react-href-var
46 | const b = ;
47 |
48 | // ok: react-href-var
49 | let x = ;
50 |
51 | // ok: react-href-var
52 | let x = ;
53 |
54 | function okTest1() {
55 | // ok: react-href-var
56 | return React.createElement("a", {href: "https://www.example.com"});
57 | }
58 |
59 | function F({ info }) {
60 | const u = info.data.url.url;
61 | // ok: react-href-var
62 | return Link
63 | }
64 |
65 | function G({ info }) {
66 | const u = info.data.url.url;
67 | // ruleid: react-href-var
68 | return Link
69 | }
--------------------------------------------------------------------------------
/assets/semgrep_rules/services/react-href-var.yaml:
--------------------------------------------------------------------------------
1 | rules:
2 | - id: react-href-var
3 | message: Detected a variable used in an anchor tag with the 'href' attribute. A malicious actor may be able to input the 'javascript:' URI, which could cause cross-site scripting (XSS). It is recommended to disallow 'javascript:' URIs within your application.
4 | metadata:
5 | cwe:
6 | - "CWE-79: Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')"
7 | owasp:
8 | - A07:2017 - Cross-Site Scripting (XSS)
9 | - A03:2021 - Injection
10 | references:
11 | - https://reactjs.org/blog/2019/08/08/react-v16.9.0.html#deprecating-javascript-urls
12 | - https://pragmaticwebsecurity.com/articles/spasecurity/react-xss-part1.html
13 | category: security
14 | confidence: LOW
15 | technology:
16 | - react
17 | license: Commons Clause License Condition v1.0[LGPL-2.1-only]
18 | cwe2022-top25: true
19 | cwe2021-top25: true
20 | subcategory:
21 | - audit
22 | likelihood: LOW
23 | impact: MEDIUM
24 | vulnerability_class:
25 | - Cross-Site-Scripting (XSS)
26 | source: https://github.com/brave/security-action/blob/main/assets/semgrep_rules/services/react-href-var.yaml
27 | original_source: https://semgrep.dev/r/typescript.react.security.audit.react-href-var.react-href-var
28 | languages:
29 | - typescript
30 | - javascript
31 | severity: WARNING
32 | mode: taint
33 | pattern-sources:
34 | - label: TAINTED
35 | patterns:
36 | - pattern-either:
37 | - pattern-inside: |
38 | function ...({..., $X, ...}) { ... }
39 | - pattern-inside: |
40 | function ...(..., $X, ...) { ... }
41 | - focus-metavariable: $X
42 | - pattern-either:
43 | - pattern: $X.$Y
44 | - pattern: $X[...]
45 | - pattern-not-inside: |
46 | $F. ... .$SANITIZEUNC(...)
47 | - label: CONCAT
48 | requires: TAINTED
49 | patterns:
50 | - pattern-either:
51 | - pattern: |
52 | `...${$X}...`
53 | - pattern: |
54 | $SANITIZE + <... $X ...>
55 | - pattern-not: |
56 | `${$X}...`
57 | - pattern-not: |
58 | $X + ...
59 | - focus-metavariable: $X
60 | - label: CLEAN
61 | by-side-effect: true
62 | patterns:
63 | - pattern-either:
64 | - pattern: $A($SOURCE)
65 | - pattern: $SANITIZE. ... .$A($SOURCE)
66 | - pattern: $A. ... .$SANITIZE($SOURCE)
67 | - focus-metavariable: $SOURCE
68 | - metavariable-regex:
69 | metavariable: $A
70 | regex: (?i)(.*valid|.*sanitiz)
71 | pattern-sinks:
72 | - requires: TAINTED and not CONCAT and not CLEAN
73 | patterns:
74 | - focus-metavariable: $X
75 | - pattern-either:
76 | - pattern: |
77 | <$EL href={$X} />
78 | - pattern: |
79 | React.createElement($EL, {href: $X})
80 | - pattern-inside: |
81 | $PARAMS = {href: $X};
82 | ...
83 | React.createElement($EL, $PARAMS);
84 | - metavariable-pattern:
85 | patterns:
86 | - pattern-not-regex: (?i)(button|SecureLink)
87 | metavariable: $EL
88 |
--------------------------------------------------------------------------------
/assets/semgrep_rules/services/ssti.go:
--------------------------------------------------------------------------------
1 | // LICENSE: Commons Clause License Condition v1.0[LGPL-2.1-only]
2 | // original source: https://github.com/returntocorp/semgrep-rules/blob/5b098c252feec688d243cef046d07597a546c25b/go/template/security/insecure-types.go
3 |
4 | package main
5 |
6 | import (
7 | "fmt"
8 | "html/template"
9 | "net/http"
10 | )
11 |
12 | type User struct {
13 | ID int
14 | Email string
15 | Password string
16 | }
17 |
18 | func match1(w http.ResponseWriter, req *http.Request) {
19 |
20 | var user1 = &User{1, "user@gmail.com", "Sup3rSecr3t123!"}
21 | query := req.URL.Query().Get("query")
22 | // ruleid:go-ssti
23 | var text = fmt.Sprintf(`
24 |
25 |
26 | SSTI
27 |
28 |
29 | Hello {{ .Email }}
30 | Search result for %s
31 |
32 | `, query)
33 | tmpl := template.New("hello")
34 | tmpl, err := tmpl.Parse(text)
35 | if err != nil {
36 | fmt.Println(err)
37 | }
38 | tmpl.Execute(w, user1)
39 | }
40 |
41 | func match2(w http.ResponseWriter, req *http.Request) {
42 |
43 | var user1 = &User{1, "user@gmail.com", "Sup3rSecr3t123!"}
44 | if err := req.ParseForm(); err != nil {
45 | fmt.Fprintf(w, "ParseForm() err: %v", err)
46 | return
47 | }
48 | query := req.Form.Get("query")
49 | // ruleid:go-ssti
50 | var text = fmt.Sprintf(`
51 |
52 |
53 | SSTI
54 |
55 |
56 | Hello {{ .Email }}
57 | Search result for %s
58 |
59 | `, query)
60 | tmpl := template.New("hello")
61 | tmpl, err := tmpl.Parse(text)
62 | if err != nil {
63 | fmt.Println(err)
64 | }
65 | tmpl.Execute(w, user1)
66 | }
67 |
68 | func no_match(w http.ResponseWriter, req *http.Request) {
69 |
70 | var user1 = &User{1, "user@gmail.com", "Sup3rSecr3t123!"}
71 | query := "constant string"
72 | // ok:go-ssti
73 | var text = fmt.Sprintf(`
74 |
75 |
76 | SSTI
77 |
78 |
79 | Hello {{ .Email }}
80 | Search result for %s
81 |
82 | `, query)
83 | tmpl := template.New("hello")
84 | tmpl, err := tmpl.Parse(text)
85 | if err != nil {
86 | fmt.Println(err)
87 | }
88 | tmpl.Execute(w, user1)
89 | }
90 |
--------------------------------------------------------------------------------
/assets/semgrep_rules/services/ssti.yaml:
--------------------------------------------------------------------------------
1 | rules:
2 | - id: go-ssti
3 | patterns:
4 | - pattern-inside: |
5 | import ("html/template")
6 | ...
7 | - pattern: $TEMPLATE = fmt.Sprintf("...", $ARG, ...)
8 | - patterns:
9 | - pattern-either:
10 | - pattern-inside: |
11 | func $FN(..., $REQ *http.Request, ...){
12 | ...
13 | }
14 | - pattern-inside: |
15 | func $FN(..., $REQ http.Request, ...){
16 | ...
17 | }
18 | - pattern-inside: |
19 | func(..., $REQ *http.Request, ...){
20 | ...
21 | }
22 | - patterns:
23 | - pattern-either:
24 | - pattern-inside: |
25 | $ARG := $REQ.URL.Query().Get(...)
26 | ...
27 | $T, $ERR := $TMPL.Parse($TEMPLATE)
28 | - pattern-inside: |
29 | $ARG := $REQ.Form.Get(...)
30 | ...
31 | $T, $ERR := $TMPL.Parse($TEMPLATE)
32 | - pattern-inside: |
33 | $ARG := $REQ.PostForm.Get(...)
34 | ...
35 | $T, $ERR := $TMPL.Parse($TEMPLATE)
36 | message: >-
37 | A server-side template injection occurs when an attacker is able to use native template syntax to inject a malicious payload into a template, which is then executed server-side. When using "html/template" always check that user inputs are validated and sanitized before included within the template.
38 | languages: [go]
39 | severity: ERROR
40 | metadata:
41 | source: https://github.com/brave/security-action/blob/main/assets/semgrep_rules/services/ssti.yaml
42 | original_source: https://github.com/returntocorp/semgrep-rules/blob/5b098c252feec688d243cef046d07597a546c25b/go/template/security/ssti.yaml
43 | category: security
44 | cwe:
45 | - 'CWE-1336: Improper Neutralization of Special Elements Used in a Template Engine'
46 | references:
47 | - https://www.onsecurity.io/blog/go-ssti-method-research/
48 | - http://blog.takemyhand.xyz/2020/05/ssti-breaking-gos-template-engine-to.html
49 | technology:
50 | - go
51 | confidence: MEDIUM
52 | subcategory:
53 | - vuln
54 | likelihood: LOW
55 | impact: HIGH
56 | license: Commons Clause License Condition v1.0[LGPL-2.1-only]
57 |
--------------------------------------------------------------------------------
/assets/semgrep_rules/services/starts-with-partial-host-py.py:
--------------------------------------------------------------------------------
1 | # ruleid: starts-with-partial-host-py
2 | my_urI[0].startswith("https://x.y")
3 |
4 | # ruleid: starts-with-partial-host-py
5 | request.url.startswith('https://example.com')
6 |
7 | # ruleid: starts-with-partial-host-py
8 | url.startswith('http://127.0.0.1:')
9 |
10 | # ok: starts-with-partial-host-py
11 | url.startswith("https://ba.na/x")
12 |
13 | # ok: starts-with-partial-host-py
14 | url.startswith("https://")
15 |
16 | # ok: starts-with-partial-host-py
17 | url.startswith("xyz://abc/https://def")
--------------------------------------------------------------------------------
/assets/semgrep_rules/services/starts-with-partial-host-py.yaml:
--------------------------------------------------------------------------------
1 | rules:
2 | - id: starts-with-partial-host-py
3 | metadata:
4 | author: Ben Caller
5 | confidence: LOW
6 | source: https://github.com/brave/security-action/blob/main/assets/semgrep_rules/services/starts-with-partial-host-py.yaml
7 | category: security
8 | patterns:
9 | - pattern: $URL.startswith("$PREFIX")
10 | - metavariable-regex:
11 | metavariable: $PREFIX
12 | regex: (?i)^https?://[^/]+$
13 | - metavariable-regex:
14 | # Avoid false positives where we actually have an origin or hostname
15 | metavariable: $URL
16 | regex: (?i).*ur[li].*
17 | message: |
18 | Add a forward-slash at the end to prevent matching `$PREFIX.e.vil` or `$PREFIX@e.vil`.
19 | Even better, properly parse the URL and match a list of origins/hosts.
20 | languages: [python]
21 | severity: WARNING
22 |
--------------------------------------------------------------------------------
/assets/semgrep_rules/services/svelte-html-usages.svelte:
--------------------------------------------------------------------------------
1 |
2 | {@html trigger}
3 |
4 | {@html
5 | i18n.replace("a", "z")
6 | }
7 |
8 |
9 | {@html safeTranslate(
10 | i18n, key, {}
11 | )}
12 |
13 | {@html MyIcon}
14 |
15 | {@html serializeJsonLD({})}
--------------------------------------------------------------------------------
/assets/semgrep_rules/services/svelte-html-usages.yaml:
--------------------------------------------------------------------------------
1 | rules:
2 | - id: svelte-html-usages
3 | metadata:
4 | author: Andrea Brancaleoni
5 | references:
6 | - https://cwe.mitre.org/data/definitions/546
7 | - https://cwe.mitre.org/data/definitions/615
8 | - https://btlr.dev/blog/how-to-find-vulnerabilities-in-code-bad-words
9 | confidence: LOW
10 | source: https://github.com/brave/security-action/blob/main/assets/semgrep_rules/services/svelte-html-usages.yaml
11 | category: security
12 | message: >-
13 | The code contains new security hotspots (`{@html expression}`) which should be checked manually by a security team member!
14 | severity: INFO
15 | languages:
16 | - generic
17 | paths:
18 | include:
19 | - "*.svelte"
20 | patterns:
21 | - pattern-regex: \{@html\s+[^A-Z]
22 | - pattern-not-regex: \{@html\s+safeTranslate\(
23 | - pattern-not-regex: \{@html\s+sanitize\(
24 | - pattern-not-regex: \{@html\s+serializeJsonLD\(
25 |
--------------------------------------------------------------------------------
/assets/semgrep_rules/services/svelte-purifyConfig-usage.svelte:
--------------------------------------------------------------------------------
1 | // ruleid: svelte-purifyConfig-usages
2 | purifyConfig(test, 3)
3 |
--------------------------------------------------------------------------------
/assets/semgrep_rules/services/svelte-purifyConfig-usage.yaml:
--------------------------------------------------------------------------------
1 | rules:
2 | - id: svelte-purifyConfig-usages
3 | metadata:
4 | author: Andrea Brancaleoni
5 | references:
6 | - https://cwe.mitre.org/data/definitions/546
7 | - https://cwe.mitre.org/data/definitions/615
8 | - https://btlr.dev/blog/how-to-find-vulnerabilities-in-code-bad-words
9 | confidence: LOW
10 | source: https://github.com/brave/security-action/blob/main/assets/semgrep_rules/services/svelte-purifyConfig-usage.yaml
11 | category: security
12 | message: >-
13 | The code contains new security hotspots (`purifyConfig`) which should be checked manually by a security team member!
14 | severity: INFO
15 | languages:
16 | - generic
17 | paths:
18 | include:
19 | - "*.svelte"
20 | - "*.ts"
21 | - "*.js"
22 | patterns:
23 | - pattern-regex: purifyConfig\([^)]+\)
24 |
--------------------------------------------------------------------------------
/assets/semgrep_rules/services/url-constructor-base.js:
--------------------------------------------------------------------------------
1 | // ruleid: url-constructor-base
2 | var unsafe = new URL(variable, "https://brave.com");
3 | // ruleid: url-constructor-base
4 | var unsafe = new URL(variable + "xyz", constantOrVariable);
5 | // ruleid: url-constructor-base
6 | var unsafe = new URL(`${variable}/xyz`, constantOrVariable);
7 | // ruleid: url-constructor-base
8 | var unsafe = new URL(`/${variable}/xyz`, constantOrVariable);
9 | // ruleid: url-constructor-base
10 | var unsafe = new URL("https://brave.com" + variable, constantOrVariable);
11 | // ruleid: url-constructor-base
12 | var unsafe = new URL("/" + variable, constantOrVariable);
13 | // ruleid: url-constructor-base
14 | var unsafe = new URL("https://" + variable, constantOrVariable);
15 | // ruleid: url-constructor-base
16 | var unsafe = new URL("file" + variable, constantOrVariable);
17 |
18 |
19 | // No base:
20 |
21 | // ok: url-constructor-base
22 | var notUnsafe0 = new URL(variable);
23 | // ok: url-constructor-base
24 | var notUnsafe1 = new URL(variable + "xyz");
25 | // ok: url-constructor-base
26 | var notUnsafe2 = new URL(`${variable}/xyz`);
27 |
28 | // Unable to start with double forward slashes, double backslashes, https:// or mess with hostname
29 |
30 | // ok: url-constructor-base
31 | var notUnsafe3 = new URL(`/const`, location.origin);
32 | // todook: url-constructor-base
33 | var notUnsafe4 = new URL("?" + variable, constantOrVariable);
34 | // todook: url-constructor-base
35 | var notUnsafe5 = new URL(`/a${variable}/xyz`, "https://brave.com");
36 | // todook: url-constructor-base
37 | var notUnsafe6 = new URL("https://not.sure/" + variable, "https://brave.com");
38 | // todook: url-constructor-base
39 | var notUnsafe7 = new URL(`#${variable}`, constantOrVariable);
40 |
41 | // ok: url-constructor-base
42 | var notUnsafe8 = new URL("https://not.sure/" + variable, "https://brave.com");
43 | if(notUnsafe8.origin !== "https://brave.com") {
44 | throw new Error("X");
45 | }
--------------------------------------------------------------------------------
/assets/semgrep_rules/services/url-constructor-base.yaml:
--------------------------------------------------------------------------------
1 | rules:
2 | - id: url-constructor-base
3 | metadata:
4 | author: Ben Caller
5 | confidence: LOW
6 | references:
7 | - https://developer.mozilla.org/en-US/docs/Web/API/URL/URL#parameters
8 | source: https://github.com/brave/security-action/blob/main/assets/semgrep_rules/services/url-constructor-base.yaml
9 | assignees: |
10 | thypon
11 | kdenhartog
12 | category: security
13 | message: Are you using the `URL(url, base)` constructor as a security control to limit the origin with base `$BASE`? The base is ignored whenever url looks like an absolute URL, e.g. when it begins `protocol://`. `\\\\` or `//x.y`. Verify that the URL's origin is as expected rather than relying on the URL constructor.
14 | severity: INFO
15 | languages:
16 | - ts
17 | - js
18 | patterns:
19 | - pattern: new URL($A, $BASE)
20 | # Exclude constant string
21 | - pattern-not: new URL("...", $BASE)
22 | # Hopefully a sensible check for the correct origin
23 | - pattern-not-inside: |
24 | $VAR = new URL($A, $BASE)
25 | ...
26 | <... $VAR.origin ...>
27 |
--------------------------------------------------------------------------------
/assets/semgrep_rules/services/var-in-href.html:
--------------------------------------------------------------------------------
1 | {% extends "layout.html" %}
2 |
3 | {% block body %}
4 |
5 | Click
6 |
7 | this
8 |
9 | link
10 | {% endblock %}
--------------------------------------------------------------------------------
/assets/semgrep_rules/services/var-in-href.yaml:
--------------------------------------------------------------------------------
1 | rules:
2 | - id: var-in-href
3 | message: Detected a template variable used in an anchor tag with the 'href' attribute. This allows a malicious actor to input the 'javascript:' URI and is subject to cross- site scripting (XSS) attacks. If using Flask, use 'url_for()' to safely generate a URL. If using Django, use the 'url' filter to safely generate a URL. If using Mustache, use a URL encoding library, or prepend a slash '/' to the variable for relative links (`href="/{{link}}"`). You may also consider setting the Content Security Policy (CSP) header.
4 | metadata:
5 | cwe:
6 | - "CWE-79: Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')"
7 | owasp:
8 | - A07:2017 - Cross-Site Scripting (XSS)
9 | - A03:2021 - Injection
10 | references:
11 | - https://flask.palletsprojects.com/en/1.1.x/security/#cross-site-scripting-xss#:~:text=javascript:%20URI
12 | - https://docs.djangoproject.com/en/3.1/ref/templates/builtins/#url
13 | - https://github.com/pugjs/pug/issues/2952
14 | - https://content-security-policy.com/
15 | - https://semgrep.dev/r/generic.html-templates.security.var-in-href.var-in-href
16 | category: security
17 | technology:
18 | - html-templates
19 | confidence: LOW
20 | cwe2022-top25: true
21 | cwe2021-top25: true
22 | subcategory:
23 | - audit
24 | likelihood: LOW
25 | impact: MEDIUM
26 | license: Commons Clause License Condition v1.0[LGPL-2.1-only]
27 | source: https://github.com/brave/security-action/blob/main/assets/semgrep_rules/services/var-in-href.yaml
28 | languages:
29 | - generic
30 | paths:
31 | include:
32 | - "*.html"
33 | - "*.mustache"
34 | - "*.hbs"
35 | severity: WARNING
36 | patterns:
37 | - pattern-inside:
38 | - pattern-either:
39 | - pattern: href = {{ ... }}
40 | - pattern: href = "{{ ... }}"
41 | - pattern: href = '{{ ... }}'
42 | - pattern-not-inside: href = {{ url_for(...) ... }}
43 | - pattern-not-inside: href = "{{ url_for(...) ... }}"
44 | - pattern-not-inside: href = '{{ url_for(...) ... }}'
45 | - pattern-not-inside: href = "/{{ ... }}"
46 | - pattern-not-inside: href = '/{{ ... }}'
47 | # exception for sanititze_url
48 | - pattern-not-regex: '.*\|\s*sanitize_url\s*\}\}.'
49 |
--------------------------------------------------------------------------------
/assets/semgrep_rules/services/var-in-script-tag.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
13 |
14 |
15 |
{{ this_is_fine }}
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/assets/semgrep_rules/services/var-in-script-tag.yaml:
--------------------------------------------------------------------------------
1 | rules:
2 | - id: var-in-script-tag
3 | message: Detected a template variable used in a script tag. Although template variables are HTML escaped, HTML escaping does not always prevent cross-site scripting (XSS) attacks when used directly in JavaScript. If you need this data on the rendered page, consider placing it in the HTML portion (outside of a script tag). Alternatively, use a JavaScript-specific encoder, such as the one available in OWASP ESAPI. For Django, you may also consider using the 'json_script' template tag and retrieving the data in your script by using the element ID (e.g., `document.getElementById`). Use the `tojson` filter for JSON.
4 | metadata:
5 | cwe:
6 | - "CWE-79: Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')"
7 | owasp:
8 | - A07:2017 - Cross-Site Scripting (XSS)
9 | - A03:2021 - Injection
10 | references:
11 | - https://adamj.eu/tech/2020/02/18/safely-including-data-for-javascript-in-a-django-template/?utm_campaign=Django%2BNewsletter&utm_medium=rss&utm_source=Django_Newsletter_12A
12 | - https://www.veracode.com/blog/secure-development/nodejs-template-engines-why-default-encoders-are-not-enough
13 | - https://github.com/ESAPI/owasp-esapi-js
14 | - https://semgrep.dev/r/generic.html-templates.security.var-in-script-tag.var-in-script-tag
15 | - https://jinja.palletsprojects.com/en/3.0.x/templates/#jinja-filters.tojson
16 | category: security
17 | technology:
18 | - html-templates
19 | confidence: LOW
20 | cwe2022-top25: true
21 | cwe2021-top25: true
22 | subcategory:
23 | - audit
24 | likelihood: LOW
25 | impact: MEDIUM
26 | license: Commons Clause License Condition v1.0[LGPL-2.1-only]
27 | source: https://github.com/brave/security-action/blob/main/assets/semgrep_rules/services/var-in-script-tag.yaml
28 | languages:
29 | - generic
30 | paths:
31 | include:
32 | - "*.mustache"
33 | - "*.hbs"
34 | - "*.html"
35 | severity: WARNING
36 | patterns:
37 | - pattern-inside:
38 | - pattern-not-inside:
30 |
31 |
--------------------------------------------------------------------------------
/t3sts/dtd/theme=🌚 dark.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/t3sts/pipaudit/pyproject.toml:
--------------------------------------------------------------------------------
1 | [project]
2 | name = "actiontest"
3 | version = "0.0.1"
4 | description = "Test of pip-audit"
5 | readme = "README.md"
6 | requires-python = ">=3.8"
7 | classifiers = [
8 | "Programming Language :: Python :: 3",
9 | ]
10 | dependencies = [
11 | "fastapi==0.104.1", "pydantic==1.10.13",
12 | "shortuuid==1.0.11",
13 | "Jinja2>=3.1.2", "transformers==4.36.2"
14 | ]
15 |
16 | [project.optional-dependencies]
17 | dev = ["black==23.7.0", "pylint==2.17.5", "polib==1.2.0"]
18 | test = [ "pytest", "pytest-asyncio", "requests", "httpx", "pytest_httpx", "time-machine", "aioresponses" ]
--------------------------------------------------------------------------------
/t3sts/pipaudit/requirements-flask.txt:
--------------------------------------------------------------------------------
1 | # This is probably ok
2 | boto3
3 |
4 | # This has a vulnerability
5 | flask==2.3.1
6 | salt==2016.3
--------------------------------------------------------------------------------
/t3sts/tf/example.tf:
--------------------------------------------------------------------------------
1 | resource "aws_vpc" "main" {
2 | cidr_block = "10.0.0.0/16"
3 | }
4 |
5 | resource "aws_security_group" "example" {
6 | name = "example"
7 | description = "Very complex Security Group for testing"
8 | vpc_id = aws_vpc.main.id
9 |
10 | tags = {
11 | "Name" = "example"
12 | }
13 | }
14 |
15 | resource "aws_security_group_rule" "example_rule" {
16 | security_group_id = aws_security_group.example.id
17 | description = "Allow all traffic from Internet which will make tfsec throw an error"
18 |
19 | type = "ingress"
20 | from_port = "0"
21 | to_port = "65535"
22 | protocol = "tcp"
23 |
24 | cidr_blocks = ["0.0.0.0/0"]
25 | ipv6_cidr_blocks = ["::/0"]
26 | }
27 |
28 |
29 | resource "azurerm_resource_group" "example" {
30 | name = "example-resources"
31 | location = "West Europe"
32 | }
33 |
34 | resource "azurerm_managed_disk" "example" {
35 | name = "acctestmd"
36 | location = "West Europe"
37 | resource_group_name = azurerm_resource_group.example.name
38 | storage_account_type = "Standard_LRS"
39 | create_option = "Empty"
40 | disk_size_gb = "1"
41 |
42 | encryption_settings {
43 | enabled = false
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/t3sts/tf/main.tf:
--------------------------------------------------------------------------------
1 | terraform {
2 | required_version = "~> 1.0"
3 |
4 | required_providers {
5 | aws = {
6 | source = "hashicorp/aws"
7 | version = "~> 4.55.0"
8 | }
9 |
10 | azurerm = {
11 | source = "hashicorp/azurerm"
12 | version = "~> 3.44.0"
13 | }
14 | }
15 | }
16 |
17 | provider "aws" {
18 | region = "eu-west-1"
19 | }
20 |
21 | provider "azurerm" {
22 | features {}
23 | }
24 |
--------------------------------------------------------------------------------