Fossil SCM
All /fileedit/ajax requests now do a CSRF check and fail with 403 if CSRF seems likely.
Commit
88a69253cd7aa0d8000715fdfa2421eb9bb036c3cacd945ed9015adf089a1ee9
Parent
b9b746e50f27b91…
2 files changed
+12
-7
+8
+12
-7
| --- src/fileedit.c | ||
| +++ src/fileedit.c | ||
| @@ -1071,18 +1071,23 @@ | ||
| 1071 | 1071 | /* |
| 1072 | 1072 | ** Performs bootstrapping common to the /fileedit_xyz AJAX routes. |
| 1073 | 1073 | ** Returns 0 if bootstrapping fails (wrong permissions), in which |
| 1074 | 1074 | ** case it has reported the error and the route should immediately |
| 1075 | 1075 | ** return. Returns true on success. |
| 1076 | +** | |
| 1077 | +** Must be passed true if the request being set up requires POST, | |
| 1078 | +** else false. | |
| 1076 | 1079 | */ |
| 1077 | -static int fileedit_ajax_boostrap(void){ | |
| 1080 | +static int fileedit_ajax_boostrap(int requirePost){ | |
| 1078 | 1081 | login_check_credentials(); |
| 1079 | 1082 | if( !g.perm.Write ){ |
| 1080 | 1083 | fileedit_ajax_error(403,"Write permissions required."); |
| 1081 | 1084 | return 0; |
| 1085 | + }else if(0==cgi_csrf_safe(requirePost)){ | |
| 1086 | + fileedit_ajax_error(403, "CSRF violation?"); | |
| 1087 | + return 0; | |
| 1082 | 1088 | } |
| 1083 | - | |
| 1084 | 1089 | return 1; |
| 1085 | 1090 | } |
| 1086 | 1091 | /* |
| 1087 | 1092 | ** Returns true if the current user is allowed to edit the given |
| 1088 | 1093 | ** filename, as determined by fileedit_is_editable(), else false, |
| @@ -1213,11 +1218,11 @@ | ||
| 1213 | 1218 | int vid, frid; |
| 1214 | 1219 | Blob content = empty_blob; |
| 1215 | 1220 | const char * zMime; |
| 1216 | 1221 | |
| 1217 | 1222 | fileedit_get_fnci_args( &zFilename, &zRev ); |
| 1218 | - if(!fileedit_ajax_boostrap() | |
| 1223 | + if(!fileedit_ajax_boostrap(0) | |
| 1219 | 1224 | || !fileedit_ajax_setup_filerev(zRev, 0, &vid, |
| 1220 | 1225 | zFilename, &frid)){ |
| 1221 | 1226 | return; |
| 1222 | 1227 | } |
| 1223 | 1228 | zMime = mimetype_from_name(zFilename); |
| @@ -1287,11 +1292,11 @@ | ||
| 1287 | 1292 | int ln = atoi(PD("ln","0")); |
| 1288 | 1293 | int iframeHeight = atoi(PD("iframe_height","40")); |
| 1289 | 1294 | Blob content = empty_blob; |
| 1290 | 1295 | const char * zRenderMode = 0; |
| 1291 | 1296 | fileedit_get_fnci_args( &zFilename, 0 ); |
| 1292 | - if(!fileedit_ajax_boostrap() | |
| 1297 | + if(!fileedit_ajax_boostrap(1) | |
| 1293 | 1298 | || !fileedit_ajax_check_filename(zFilename)){ |
| 1294 | 1299 | return; |
| 1295 | 1300 | } |
| 1296 | 1301 | cgi_set_content_type("text/html"); |
| 1297 | 1302 | blob_init(&content, zContent, -1); |
| @@ -1370,11 +1375,11 @@ | ||
| 1370 | 1375 | }else if(1==iFlag){ |
| 1371 | 1376 | diffFlags |= DIFF_IGNORE_EOLWS; |
| 1372 | 1377 | } |
| 1373 | 1378 | diffFlags |= DIFF_STRIP_EOLCR; |
| 1374 | 1379 | fileedit_get_fnci_args( &zFilename, &zRev ); |
| 1375 | - if(!fileedit_ajax_boostrap() | |
| 1380 | + if(!fileedit_ajax_boostrap(1) | |
| 1376 | 1381 | || !fileedit_ajax_setup_filerev(zRev, &zRevUuid, &vid, |
| 1377 | 1382 | zFilename, &frid)){ |
| 1378 | 1383 | return; |
| 1379 | 1384 | } |
| 1380 | 1385 | if(!zContent){ |
| @@ -1557,11 +1562,11 @@ | ||
| 1557 | 1562 | const char * zCi = PD("checkin",P("ci")); |
| 1558 | 1563 | Blob sql = empty_blob; |
| 1559 | 1564 | Stmt q = empty_Stmt; |
| 1560 | 1565 | int i = 0; |
| 1561 | 1566 | |
| 1562 | - if(!fileedit_ajax_boostrap()){ | |
| 1567 | + if(!fileedit_ajax_boostrap(0)){ | |
| 1563 | 1568 | return; |
| 1564 | 1569 | } |
| 1565 | 1570 | cgi_set_content_type("application/json"); |
| 1566 | 1571 | if(zCi!=0){ |
| 1567 | 1572 | char * zCiFull = 0; |
| @@ -1661,11 +1666,11 @@ | ||
| 1661 | 1666 | int newVid = 0; /* new version's RID */ |
| 1662 | 1667 | char * zNewUuid = 0; /* newVid's UUID */ |
| 1663 | 1668 | char const * zMimetype; |
| 1664 | 1669 | char * zBranch = 0; |
| 1665 | 1670 | |
| 1666 | - if(!fileedit_ajax_boostrap()){ | |
| 1671 | + if(!fileedit_ajax_boostrap(1)){ | |
| 1667 | 1672 | return; |
| 1668 | 1673 | } |
| 1669 | 1674 | db_begin_transaction(); |
| 1670 | 1675 | CheckinMiniInfo_init(&cimi); |
| 1671 | 1676 | rc = fileedit_setup_cimi_from_p(&cimi, &err, 0); |
| 1672 | 1677 |
| --- src/fileedit.c | |
| +++ src/fileedit.c | |
| @@ -1071,18 +1071,23 @@ | |
| 1071 | /* |
| 1072 | ** Performs bootstrapping common to the /fileedit_xyz AJAX routes. |
| 1073 | ** Returns 0 if bootstrapping fails (wrong permissions), in which |
| 1074 | ** case it has reported the error and the route should immediately |
| 1075 | ** return. Returns true on success. |
| 1076 | */ |
| 1077 | static int fileedit_ajax_boostrap(void){ |
| 1078 | login_check_credentials(); |
| 1079 | if( !g.perm.Write ){ |
| 1080 | fileedit_ajax_error(403,"Write permissions required."); |
| 1081 | return 0; |
| 1082 | } |
| 1083 | |
| 1084 | return 1; |
| 1085 | } |
| 1086 | /* |
| 1087 | ** Returns true if the current user is allowed to edit the given |
| 1088 | ** filename, as determined by fileedit_is_editable(), else false, |
| @@ -1213,11 +1218,11 @@ | |
| 1213 | int vid, frid; |
| 1214 | Blob content = empty_blob; |
| 1215 | const char * zMime; |
| 1216 | |
| 1217 | fileedit_get_fnci_args( &zFilename, &zRev ); |
| 1218 | if(!fileedit_ajax_boostrap() |
| 1219 | || !fileedit_ajax_setup_filerev(zRev, 0, &vid, |
| 1220 | zFilename, &frid)){ |
| 1221 | return; |
| 1222 | } |
| 1223 | zMime = mimetype_from_name(zFilename); |
| @@ -1287,11 +1292,11 @@ | |
| 1287 | int ln = atoi(PD("ln","0")); |
| 1288 | int iframeHeight = atoi(PD("iframe_height","40")); |
| 1289 | Blob content = empty_blob; |
| 1290 | const char * zRenderMode = 0; |
| 1291 | fileedit_get_fnci_args( &zFilename, 0 ); |
| 1292 | if(!fileedit_ajax_boostrap() |
| 1293 | || !fileedit_ajax_check_filename(zFilename)){ |
| 1294 | return; |
| 1295 | } |
| 1296 | cgi_set_content_type("text/html"); |
| 1297 | blob_init(&content, zContent, -1); |
| @@ -1370,11 +1375,11 @@ | |
| 1370 | }else if(1==iFlag){ |
| 1371 | diffFlags |= DIFF_IGNORE_EOLWS; |
| 1372 | } |
| 1373 | diffFlags |= DIFF_STRIP_EOLCR; |
| 1374 | fileedit_get_fnci_args( &zFilename, &zRev ); |
| 1375 | if(!fileedit_ajax_boostrap() |
| 1376 | || !fileedit_ajax_setup_filerev(zRev, &zRevUuid, &vid, |
| 1377 | zFilename, &frid)){ |
| 1378 | return; |
| 1379 | } |
| 1380 | if(!zContent){ |
| @@ -1557,11 +1562,11 @@ | |
| 1557 | const char * zCi = PD("checkin",P("ci")); |
| 1558 | Blob sql = empty_blob; |
| 1559 | Stmt q = empty_Stmt; |
| 1560 | int i = 0; |
| 1561 | |
| 1562 | if(!fileedit_ajax_boostrap()){ |
| 1563 | return; |
| 1564 | } |
| 1565 | cgi_set_content_type("application/json"); |
| 1566 | if(zCi!=0){ |
| 1567 | char * zCiFull = 0; |
| @@ -1661,11 +1666,11 @@ | |
| 1661 | int newVid = 0; /* new version's RID */ |
| 1662 | char * zNewUuid = 0; /* newVid's UUID */ |
| 1663 | char const * zMimetype; |
| 1664 | char * zBranch = 0; |
| 1665 | |
| 1666 | if(!fileedit_ajax_boostrap()){ |
| 1667 | return; |
| 1668 | } |
| 1669 | db_begin_transaction(); |
| 1670 | CheckinMiniInfo_init(&cimi); |
| 1671 | rc = fileedit_setup_cimi_from_p(&cimi, &err, 0); |
| 1672 |
| --- src/fileedit.c | |
| +++ src/fileedit.c | |
| @@ -1071,18 +1071,23 @@ | |
| 1071 | /* |
| 1072 | ** Performs bootstrapping common to the /fileedit_xyz AJAX routes. |
| 1073 | ** Returns 0 if bootstrapping fails (wrong permissions), in which |
| 1074 | ** case it has reported the error and the route should immediately |
| 1075 | ** return. Returns true on success. |
| 1076 | ** |
| 1077 | ** Must be passed true if the request being set up requires POST, |
| 1078 | ** else false. |
| 1079 | */ |
| 1080 | static int fileedit_ajax_boostrap(int requirePost){ |
| 1081 | login_check_credentials(); |
| 1082 | if( !g.perm.Write ){ |
| 1083 | fileedit_ajax_error(403,"Write permissions required."); |
| 1084 | return 0; |
| 1085 | }else if(0==cgi_csrf_safe(requirePost)){ |
| 1086 | fileedit_ajax_error(403, "CSRF violation?"); |
| 1087 | return 0; |
| 1088 | } |
| 1089 | return 1; |
| 1090 | } |
| 1091 | /* |
| 1092 | ** Returns true if the current user is allowed to edit the given |
| 1093 | ** filename, as determined by fileedit_is_editable(), else false, |
| @@ -1213,11 +1218,11 @@ | |
| 1218 | int vid, frid; |
| 1219 | Blob content = empty_blob; |
| 1220 | const char * zMime; |
| 1221 | |
| 1222 | fileedit_get_fnci_args( &zFilename, &zRev ); |
| 1223 | if(!fileedit_ajax_boostrap(0) |
| 1224 | || !fileedit_ajax_setup_filerev(zRev, 0, &vid, |
| 1225 | zFilename, &frid)){ |
| 1226 | return; |
| 1227 | } |
| 1228 | zMime = mimetype_from_name(zFilename); |
| @@ -1287,11 +1292,11 @@ | |
| 1292 | int ln = atoi(PD("ln","0")); |
| 1293 | int iframeHeight = atoi(PD("iframe_height","40")); |
| 1294 | Blob content = empty_blob; |
| 1295 | const char * zRenderMode = 0; |
| 1296 | fileedit_get_fnci_args( &zFilename, 0 ); |
| 1297 | if(!fileedit_ajax_boostrap(1) |
| 1298 | || !fileedit_ajax_check_filename(zFilename)){ |
| 1299 | return; |
| 1300 | } |
| 1301 | cgi_set_content_type("text/html"); |
| 1302 | blob_init(&content, zContent, -1); |
| @@ -1370,11 +1375,11 @@ | |
| 1375 | }else if(1==iFlag){ |
| 1376 | diffFlags |= DIFF_IGNORE_EOLWS; |
| 1377 | } |
| 1378 | diffFlags |= DIFF_STRIP_EOLCR; |
| 1379 | fileedit_get_fnci_args( &zFilename, &zRev ); |
| 1380 | if(!fileedit_ajax_boostrap(1) |
| 1381 | || !fileedit_ajax_setup_filerev(zRev, &zRevUuid, &vid, |
| 1382 | zFilename, &frid)){ |
| 1383 | return; |
| 1384 | } |
| 1385 | if(!zContent){ |
| @@ -1557,11 +1562,11 @@ | |
| 1562 | const char * zCi = PD("checkin",P("ci")); |
| 1563 | Blob sql = empty_blob; |
| 1564 | Stmt q = empty_Stmt; |
| 1565 | int i = 0; |
| 1566 | |
| 1567 | if(!fileedit_ajax_boostrap(0)){ |
| 1568 | return; |
| 1569 | } |
| 1570 | cgi_set_content_type("application/json"); |
| 1571 | if(zCi!=0){ |
| 1572 | char * zCiFull = 0; |
| @@ -1661,11 +1666,11 @@ | |
| 1666 | int newVid = 0; /* new version's RID */ |
| 1667 | char * zNewUuid = 0; /* newVid's UUID */ |
| 1668 | char const * zMimetype; |
| 1669 | char * zBranch = 0; |
| 1670 | |
| 1671 | if(!fileedit_ajax_boostrap(1)){ |
| 1672 | return; |
| 1673 | } |
| 1674 | db_begin_transaction(); |
| 1675 | CheckinMiniInfo_init(&cimi); |
| 1676 | rc = fileedit_setup_cimi_from_p(&cimi, &err, 0); |
| 1677 |
+8
| --- src/style.c | ||
| +++ src/style.c | ||
| @@ -1590,10 +1590,18 @@ | ||
| 1590 | 1590 | get_version(), g.zTop); |
| 1591 | 1591 | /* fossil.config = {...various config-level options...} */ |
| 1592 | 1592 | CX("window.fossil.config = {" |
| 1593 | 1593 | "hashDigits: %d, hashDigitsUrl: %d" |
| 1594 | 1594 | "};\n", hash_digits(0), hash_digits(1)); |
| 1595 | +#if 0 | |
| 1596 | + /* Is it safe to emit the CSRF token here? Some pages add it | |
| 1597 | + ** as a hidden form field. */ | |
| 1598 | + if(g.zCsrfToken[0]!=0){ | |
| 1599 | + CX("window.fossil.csrfToken = %!j;\n", | |
| 1600 | + g.zCsrfToken); | |
| 1601 | + } | |
| 1602 | +#endif | |
| 1595 | 1603 | /* |
| 1596 | 1604 | ** fossil.page holds info about the current page. This is also |
| 1597 | 1605 | ** where the current page "should" store any of its own |
| 1598 | 1606 | ** page-specific state, and it is reserved for that purpose. |
| 1599 | 1607 | */ |
| 1600 | 1608 |
| --- src/style.c | |
| +++ src/style.c | |
| @@ -1590,10 +1590,18 @@ | |
| 1590 | get_version(), g.zTop); |
| 1591 | /* fossil.config = {...various config-level options...} */ |
| 1592 | CX("window.fossil.config = {" |
| 1593 | "hashDigits: %d, hashDigitsUrl: %d" |
| 1594 | "};\n", hash_digits(0), hash_digits(1)); |
| 1595 | /* |
| 1596 | ** fossil.page holds info about the current page. This is also |
| 1597 | ** where the current page "should" store any of its own |
| 1598 | ** page-specific state, and it is reserved for that purpose. |
| 1599 | */ |
| 1600 |
| --- src/style.c | |
| +++ src/style.c | |
| @@ -1590,10 +1590,18 @@ | |
| 1590 | get_version(), g.zTop); |
| 1591 | /* fossil.config = {...various config-level options...} */ |
| 1592 | CX("window.fossil.config = {" |
| 1593 | "hashDigits: %d, hashDigitsUrl: %d" |
| 1594 | "};\n", hash_digits(0), hash_digits(1)); |
| 1595 | #if 0 |
| 1596 | /* Is it safe to emit the CSRF token here? Some pages add it |
| 1597 | ** as a hidden form field. */ |
| 1598 | if(g.zCsrfToken[0]!=0){ |
| 1599 | CX("window.fossil.csrfToken = %!j;\n", |
| 1600 | g.zCsrfToken); |
| 1601 | } |
| 1602 | #endif |
| 1603 | /* |
| 1604 | ** fossil.page holds info about the current page. This is also |
| 1605 | ** where the current page "should" store any of its own |
| 1606 | ** page-specific state, and it is reserved for that purpose. |
| 1607 | */ |
| 1608 |