Fossil SCM

Improvements and simplifications to anti-robot defenses.

drh 2025-08-16 13:59 trunk merge
Commit 16b33097fe60eac71f84a7ca28362d36da76a1ff5748bb132fc1bcdf73ca4476
--- src/browse.c
+++ src/browse.c
@@ -1162,11 +1162,10 @@
11621162
int showId = PB("showid");
11631163
Stmt q1, q2;
11641164
double baseTime;
11651165
login_check_credentials();
11661166
if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
1167
- if( exclude_spiders(0) ) return;
11681167
zName = P("name");
11691168
if( zName==0 ) zName = "tip";
11701169
rid = symbolic_name_to_rid(zName, "ci");
11711170
if( rid==0 ){
11721171
fossil_fatal("not a valid check-in: %s", zName);
11731172
--- src/browse.c
+++ src/browse.c
@@ -1162,11 +1162,10 @@
1162 int showId = PB("showid");
1163 Stmt q1, q2;
1164 double baseTime;
1165 login_check_credentials();
1166 if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
1167 if( exclude_spiders(0) ) return;
1168 zName = P("name");
1169 if( zName==0 ) zName = "tip";
1170 rid = symbolic_name_to_rid(zName, "ci");
1171 if( rid==0 ){
1172 fossil_fatal("not a valid check-in: %s", zName);
1173
--- src/browse.c
+++ src/browse.c
@@ -1162,11 +1162,10 @@
1162 int showId = PB("showid");
1163 Stmt q1, q2;
1164 double baseTime;
1165 login_check_credentials();
1166 if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
 
1167 zName = P("name");
1168 if( zName==0 ) zName = "tip";
1169 rid = symbolic_name_to_rid(zName, "ci");
1170 if( rid==0 ){
1171 fossil_fatal("not a valid check-in: %s", zName);
1172
+12 -8
--- src/captcha.c
+++ src/captcha.c
@@ -744,11 +744,11 @@
744744
(void)exclude_spiders(1);
745745
@ <hr><p>The captcha is shown above. Add a name=HEX query parameter
746746
@ to see how HEX would be rendered in the current captcha font.
747747
@ <h2>Debug/Testing Values:</h2>
748748
@ <ul>
749
- @ <li> g.isHuman = %d(g.isHuman)
749
+ @ <li> g.isRobot = %d(g.isRobot)
750750
@ <li> g.zLogin = %h(g.zLogin)
751751
@ <li> login_cookie_welformed() = %d(login_cookie_wellformed())
752752
@ <li> captcha_is_correct(1) = %d(captcha_is_correct(1)).
753753
@ </ul>
754754
style_finish_page();
@@ -759,10 +759,18 @@
759759
@ %s(captcha_render(zPw))
760760
@ </pre>
761761
style_finish_page();
762762
}
763763
}
764
+
765
+/*
766
+** WEBPAGE: honeypot
767
+** This page is a honeypot for spiders and bots.
768
+*/
769
+void honeypot_page(void){
770
+ (void)exclude_spiders(0);
771
+}
764772
765773
/*
766774
** Check to see if the current request is coming from an agent that
767775
** self-identifies as a spider.
768776
**
@@ -776,27 +784,23 @@
776784
** If the bTest argument is non-zero, then show the captcha regardless of
777785
** how the agent identifies. This is used for testing only.
778786
*/
779787
int exclude_spiders(int bTest){
780788
if( !bTest ){
781
- if( g.isHuman ) return 0; /* This user has already proven human */
782789
if( g.zLogin!=0 ) return 0; /* Logged in. Consider them human */
783790
if( login_cookie_wellformed() ){
784791
/* Logged into another member of the login group */
785792
return 0;
786793
}
787794
}
788795
789796
/* This appears to be a spider. Offer the captcha */
790797
style_set_current_feature("captcha");
791
- style_header("I think you are a robot");
798
+ style_header("Captcha");
792799
style_submenu_enable(0);
793800
@ <form method='POST' action='%R/ityaar'>
794
- @ <p>You seem like a robot.
795
- @
796
- @ <p>If you are human, you can prove that by solving the captcha below,
797
- @ after which you will be allowed to proceed.
801
+ @ <h2>Prove that you are human:
798802
if( bTest ){
799803
@ <input type="hidden" name="istest" value="1">
800804
}
801805
captcha_generate(3);
802806
@ </form>
@@ -830,11 +834,11 @@
830834
}
831835
cgi_append_header("X-Robot: 0\r\n");
832836
}
833837
login_redirect_to_g();
834838
}else{
835
- g.isHuman = 0;
839
+ g.isRobot = 1;
836840
(void)exclude_spiders(bTest);
837841
if( bTest ){
838842
@ <hr><p>Wrong code. Try again
839843
style_finish_page();
840844
}
841845
--- src/captcha.c
+++ src/captcha.c
@@ -744,11 +744,11 @@
744 (void)exclude_spiders(1);
745 @ <hr><p>The captcha is shown above. Add a name=HEX query parameter
746 @ to see how HEX would be rendered in the current captcha font.
747 @ <h2>Debug/Testing Values:</h2>
748 @ <ul>
749 @ <li> g.isHuman = %d(g.isHuman)
750 @ <li> g.zLogin = %h(g.zLogin)
751 @ <li> login_cookie_welformed() = %d(login_cookie_wellformed())
752 @ <li> captcha_is_correct(1) = %d(captcha_is_correct(1)).
753 @ </ul>
754 style_finish_page();
@@ -759,10 +759,18 @@
759 @ %s(captcha_render(zPw))
760 @ </pre>
761 style_finish_page();
762 }
763 }
 
 
 
 
 
 
 
 
764
765 /*
766 ** Check to see if the current request is coming from an agent that
767 ** self-identifies as a spider.
768 **
@@ -776,27 +784,23 @@
776 ** If the bTest argument is non-zero, then show the captcha regardless of
777 ** how the agent identifies. This is used for testing only.
778 */
779 int exclude_spiders(int bTest){
780 if( !bTest ){
781 if( g.isHuman ) return 0; /* This user has already proven human */
782 if( g.zLogin!=0 ) return 0; /* Logged in. Consider them human */
783 if( login_cookie_wellformed() ){
784 /* Logged into another member of the login group */
785 return 0;
786 }
787 }
788
789 /* This appears to be a spider. Offer the captcha */
790 style_set_current_feature("captcha");
791 style_header("I think you are a robot");
792 style_submenu_enable(0);
793 @ <form method='POST' action='%R/ityaar'>
794 @ <p>You seem like a robot.
795 @
796 @ <p>If you are human, you can prove that by solving the captcha below,
797 @ after which you will be allowed to proceed.
798 if( bTest ){
799 @ <input type="hidden" name="istest" value="1">
800 }
801 captcha_generate(3);
802 @ </form>
@@ -830,11 +834,11 @@
830 }
831 cgi_append_header("X-Robot: 0\r\n");
832 }
833 login_redirect_to_g();
834 }else{
835 g.isHuman = 0;
836 (void)exclude_spiders(bTest);
837 if( bTest ){
838 @ <hr><p>Wrong code. Try again
839 style_finish_page();
840 }
841
--- src/captcha.c
+++ src/captcha.c
@@ -744,11 +744,11 @@
744 (void)exclude_spiders(1);
745 @ <hr><p>The captcha is shown above. Add a name=HEX query parameter
746 @ to see how HEX would be rendered in the current captcha font.
747 @ <h2>Debug/Testing Values:</h2>
748 @ <ul>
749 @ <li> g.isRobot = %d(g.isRobot)
750 @ <li> g.zLogin = %h(g.zLogin)
751 @ <li> login_cookie_welformed() = %d(login_cookie_wellformed())
752 @ <li> captcha_is_correct(1) = %d(captcha_is_correct(1)).
753 @ </ul>
754 style_finish_page();
@@ -759,10 +759,18 @@
759 @ %s(captcha_render(zPw))
760 @ </pre>
761 style_finish_page();
762 }
763 }
764
765 /*
766 ** WEBPAGE: honeypot
767 ** This page is a honeypot for spiders and bots.
768 */
769 void honeypot_page(void){
770 (void)exclude_spiders(0);
771 }
772
773 /*
774 ** Check to see if the current request is coming from an agent that
775 ** self-identifies as a spider.
776 **
@@ -776,27 +784,23 @@
784 ** If the bTest argument is non-zero, then show the captcha regardless of
785 ** how the agent identifies. This is used for testing only.
786 */
787 int exclude_spiders(int bTest){
788 if( !bTest ){
 
789 if( g.zLogin!=0 ) return 0; /* Logged in. Consider them human */
790 if( login_cookie_wellformed() ){
791 /* Logged into another member of the login group */
792 return 0;
793 }
794 }
795
796 /* This appears to be a spider. Offer the captcha */
797 style_set_current_feature("captcha");
798 style_header("Captcha");
799 style_submenu_enable(0);
800 @ <form method='POST' action='%R/ityaar'>
801 @ <h2>Prove that you are human:
 
 
 
802 if( bTest ){
803 @ <input type="hidden" name="istest" value="1">
804 }
805 captcha_generate(3);
806 @ </form>
@@ -830,11 +834,11 @@
834 }
835 cgi_append_header("X-Robot: 0\r\n");
836 }
837 login_redirect_to_g();
838 }else{
839 g.isRobot = 1;
840 (void)exclude_spiders(bTest);
841 if( bTest ){
842 @ <hr><p>Wrong code. Try again
843 style_finish_page();
844 }
845
+1 -2
--- src/diff.c
+++ src/diff.c
@@ -3788,12 +3788,11 @@
37883788
int bBlame = g.zPath[0]!='a';/* True for BLAME output. False for ANNOTATE. */
37893789
37903790
/* Gather query parameters */
37913791
login_check_credentials();
37923792
if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
3793
- if( exclude_spiders(0) ) return;
3794
- if( robot_squelch(990) ) return;
3793
+ if( robot_restrict("annotate") ) return;
37953794
fossil_nice_default();
37963795
zFilename = P("filename");
37973796
zRevision = PD("checkin",0);
37983797
zOrigin = P("origin");
37993798
zLimit = P("limit");
38003799
--- src/diff.c
+++ src/diff.c
@@ -3788,12 +3788,11 @@
3788 int bBlame = g.zPath[0]!='a';/* True for BLAME output. False for ANNOTATE. */
3789
3790 /* Gather query parameters */
3791 login_check_credentials();
3792 if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
3793 if( exclude_spiders(0) ) return;
3794 if( robot_squelch(990) ) return;
3795 fossil_nice_default();
3796 zFilename = P("filename");
3797 zRevision = PD("checkin",0);
3798 zOrigin = P("origin");
3799 zLimit = P("limit");
3800
--- src/diff.c
+++ src/diff.c
@@ -3788,12 +3788,11 @@
3788 int bBlame = g.zPath[0]!='a';/* True for BLAME output. False for ANNOTATE. */
3789
3790 /* Gather query parameters */
3791 login_check_credentials();
3792 if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
3793 if( robot_restrict("annotate") ) return;
 
3794 fossil_nice_default();
3795 zFilename = P("filename");
3796 zRevision = PD("checkin",0);
3797 zOrigin = P("origin");
3798 zLimit = P("limit");
3799
+1 -1
--- src/diffcmd.c
+++ src/diffcmd.c
@@ -1522,11 +1522,11 @@
15221522
DiffConfig DCfg;
15231523
cgi_check_for_malice();
15241524
login_check_credentials();
15251525
if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
15261526
if( zFrom==0 || zTo==0 ) fossil_redirect_home();
1527
- if( robot_squelch(800) ) return;
1527
+ if( robot_restrict("diff") ) return;
15281528
15291529
fossil_nice_default();
15301530
cgi_set_content_type("text/plain");
15311531
diff_config_init(&DCfg, DIFF_VERBOSE);
15321532
diff_two_versions(zFrom, zTo, &DCfg, 0);
15331533
--- src/diffcmd.c
+++ src/diffcmd.c
@@ -1522,11 +1522,11 @@
1522 DiffConfig DCfg;
1523 cgi_check_for_malice();
1524 login_check_credentials();
1525 if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
1526 if( zFrom==0 || zTo==0 ) fossil_redirect_home();
1527 if( robot_squelch(800) ) return;
1528
1529 fossil_nice_default();
1530 cgi_set_content_type("text/plain");
1531 diff_config_init(&DCfg, DIFF_VERBOSE);
1532 diff_two_versions(zFrom, zTo, &DCfg, 0);
1533
--- src/diffcmd.c
+++ src/diffcmd.c
@@ -1522,11 +1522,11 @@
1522 DiffConfig DCfg;
1523 cgi_check_for_malice();
1524 login_check_credentials();
1525 if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
1526 if( zFrom==0 || zTo==0 ) fossil_redirect_home();
1527 if( robot_restrict("diff") ) return;
1528
1529 fossil_nice_default();
1530 cgi_set_content_type("text/plain");
1531 diff_config_init(&DCfg, DIFF_VERBOSE);
1532 diff_two_versions(zFrom, zTo, &DCfg, 0);
1533
+4 -9
--- src/info.c
+++ src/info.c
@@ -1421,11 +1421,11 @@
14211421
Blob qpGlob; /* glob= query parameter for generated links */
14221422
int bInvert = PB("inv");
14231423
14241424
login_check_credentials();
14251425
if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
1426
- if( robot_squelch(950) ) return;
1426
+ if( robot_restrict("diff") ) return;
14271427
login_anonymous_available();
14281428
fossil_nice_default();
14291429
blob_init(&qp, 0, 0);
14301430
blob_init(&qpGlob, 0, 0);
14311431
diffType = preferred_diff_type();
@@ -1975,11 +1975,11 @@
19751975
int verbose = PB("verbose");
19761976
DiffConfig DCfg;
19771977
19781978
login_check_credentials();
19791979
if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
1980
- if( robot_squelch(800) ) return;
1980
+ if( robot_restrict("diff") ) return;
19811981
diff_config_init(&DCfg, 0);
19821982
diffType = preferred_diff_type();
19831983
if( P("from") && P("to") ){
19841984
v1 = artifact_from_ci_and_filename("from");
19851985
v2 = artifact_from_ci_and_filename("to");
@@ -2416,15 +2416,15 @@
24162416
object_description(rid, objdescFlags, 0, &downloadName);
24172417
style_submenu_element("Download", "%R/raw/%s?at=%T",
24182418
zUuid, file_tail(blob_str(&downloadName)));
24192419
@ <hr>
24202420
content_get(rid, &content);
2421
- if( !g.isHuman ){
2421
+ if( blob_size(&content)>100000 ){
24222422
/* Prevent robots from running hexdump on megabyte-sized source files
24232423
** and there by eating up lots of CPU time and bandwidth. There is
24242424
** no good reason for a robot to need a hexdump. */
2425
- @ <p>A hex dump of this file is not available.
2425
+ @ <p>A hex dump of this file is not available because it is too large.
24262426
@ Please download the raw binary file and generate a hex dump yourself.</p>
24272427
}else{
24282428
@ <blockquote><pre>
24292429
hexdump(&content);
24302430
@ </pre></blockquote>
@@ -2702,24 +2702,19 @@
27022702
HQuery url;
27032703
char *zCIUuid = 0;
27042704
int isSymbolicCI = 0; /* ci= exists and is a symbolic name, not a hash */
27052705
int isBranchCI = 0; /* ci= refers to a branch name */
27062706
char *zHeader = 0;
2707
- int iCost;
27082707
27092708
login_check_credentials();
27102709
if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
27112710
cgi_check_for_malice();
27122711
style_set_current_feature("artifact");
27132712
if( fossil_strcmp(g.zPath, "docfile")==0 ){
27142713
isFile = 1;
27152714
docOnly = 1;
27162715
}
2717
- iCost = 200;
2718
- if( isFile ) iCost += 100;
2719
- if( zCI ) iCost += 100;
2720
- if( robot_squelch(iCost) ) return;
27212716
27222717
/* Capture and normalize the name= and ci= query parameters */
27232718
if( zName==0 ){
27242719
zName = P("filename");
27252720
if( zName==0 ){
27262721
--- src/info.c
+++ src/info.c
@@ -1421,11 +1421,11 @@
1421 Blob qpGlob; /* glob= query parameter for generated links */
1422 int bInvert = PB("inv");
1423
1424 login_check_credentials();
1425 if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
1426 if( robot_squelch(950) ) return;
1427 login_anonymous_available();
1428 fossil_nice_default();
1429 blob_init(&qp, 0, 0);
1430 blob_init(&qpGlob, 0, 0);
1431 diffType = preferred_diff_type();
@@ -1975,11 +1975,11 @@
1975 int verbose = PB("verbose");
1976 DiffConfig DCfg;
1977
1978 login_check_credentials();
1979 if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
1980 if( robot_squelch(800) ) return;
1981 diff_config_init(&DCfg, 0);
1982 diffType = preferred_diff_type();
1983 if( P("from") && P("to") ){
1984 v1 = artifact_from_ci_and_filename("from");
1985 v2 = artifact_from_ci_and_filename("to");
@@ -2416,15 +2416,15 @@
2416 object_description(rid, objdescFlags, 0, &downloadName);
2417 style_submenu_element("Download", "%R/raw/%s?at=%T",
2418 zUuid, file_tail(blob_str(&downloadName)));
2419 @ <hr>
2420 content_get(rid, &content);
2421 if( !g.isHuman ){
2422 /* Prevent robots from running hexdump on megabyte-sized source files
2423 ** and there by eating up lots of CPU time and bandwidth. There is
2424 ** no good reason for a robot to need a hexdump. */
2425 @ <p>A hex dump of this file is not available.
2426 @ Please download the raw binary file and generate a hex dump yourself.</p>
2427 }else{
2428 @ <blockquote><pre>
2429 hexdump(&content);
2430 @ </pre></blockquote>
@@ -2702,24 +2702,19 @@
2702 HQuery url;
2703 char *zCIUuid = 0;
2704 int isSymbolicCI = 0; /* ci= exists and is a symbolic name, not a hash */
2705 int isBranchCI = 0; /* ci= refers to a branch name */
2706 char *zHeader = 0;
2707 int iCost;
2708
2709 login_check_credentials();
2710 if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
2711 cgi_check_for_malice();
2712 style_set_current_feature("artifact");
2713 if( fossil_strcmp(g.zPath, "docfile")==0 ){
2714 isFile = 1;
2715 docOnly = 1;
2716 }
2717 iCost = 200;
2718 if( isFile ) iCost += 100;
2719 if( zCI ) iCost += 100;
2720 if( robot_squelch(iCost) ) return;
2721
2722 /* Capture and normalize the name= and ci= query parameters */
2723 if( zName==0 ){
2724 zName = P("filename");
2725 if( zName==0 ){
2726
--- src/info.c
+++ src/info.c
@@ -1421,11 +1421,11 @@
1421 Blob qpGlob; /* glob= query parameter for generated links */
1422 int bInvert = PB("inv");
1423
1424 login_check_credentials();
1425 if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
1426 if( robot_restrict("diff") ) return;
1427 login_anonymous_available();
1428 fossil_nice_default();
1429 blob_init(&qp, 0, 0);
1430 blob_init(&qpGlob, 0, 0);
1431 diffType = preferred_diff_type();
@@ -1975,11 +1975,11 @@
1975 int verbose = PB("verbose");
1976 DiffConfig DCfg;
1977
1978 login_check_credentials();
1979 if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
1980 if( robot_restrict("diff") ) return;
1981 diff_config_init(&DCfg, 0);
1982 diffType = preferred_diff_type();
1983 if( P("from") && P("to") ){
1984 v1 = artifact_from_ci_and_filename("from");
1985 v2 = artifact_from_ci_and_filename("to");
@@ -2416,15 +2416,15 @@
2416 object_description(rid, objdescFlags, 0, &downloadName);
2417 style_submenu_element("Download", "%R/raw/%s?at=%T",
2418 zUuid, file_tail(blob_str(&downloadName)));
2419 @ <hr>
2420 content_get(rid, &content);
2421 if( blob_size(&content)>100000 ){
2422 /* Prevent robots from running hexdump on megabyte-sized source files
2423 ** and there by eating up lots of CPU time and bandwidth. There is
2424 ** no good reason for a robot to need a hexdump. */
2425 @ <p>A hex dump of this file is not available because it is too large.
2426 @ Please download the raw binary file and generate a hex dump yourself.</p>
2427 }else{
2428 @ <blockquote><pre>
2429 hexdump(&content);
2430 @ </pre></blockquote>
@@ -2702,24 +2702,19 @@
2702 HQuery url;
2703 char *zCIUuid = 0;
2704 int isSymbolicCI = 0; /* ci= exists and is a symbolic name, not a hash */
2705 int isBranchCI = 0; /* ci= refers to a branch name */
2706 char *zHeader = 0;
 
2707
2708 login_check_credentials();
2709 if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
2710 cgi_check_for_malice();
2711 style_set_current_feature("artifact");
2712 if( fossil_strcmp(g.zPath, "docfile")==0 ){
2713 isFile = 1;
2714 docOnly = 1;
2715 }
 
 
 
 
2716
2717 /* Capture and normalize the name= and ci= query parameters */
2718 if( zName==0 ){
2719 zName = P("filename");
2720 if( zName==0 ){
2721
+11 -96
--- src/login.c
+++ src/login.c
@@ -1303,98 +1303,10 @@
13031303
}
13041304
fossil_free(zDecode);
13051305
return uid;
13061306
}
13071307
1308
-/*
1309
-** SETTING: robot-restrict width=40 block-text
1310
-** The VALUE of this setting is a list of GLOB patterns that match
1311
-** pages for which complex HTTP requests from robots should be disallowed.
1312
-** The recommended value for this setting is:
1313
-**
1314
-** timeline,vdiff,fdiff,annotate,blame
1315
-**
1316
-*/
1317
-
1318
-/*
1319
-** Check to see if the current HTTP request is a complex request that
1320
-** is coming from a robot and if access should restricted for such robots.
1321
-** For the purposes of this module, a "complex request" is an HTTP
1322
-** request with one or more query parameters other than "name".
1323
-**
1324
-** If this routine determines that robots should be restricted, then
1325
-** this routine publishes a redirect to the honeypot and exits without
1326
-** returning to the caller.
1327
-**
1328
-** This routine believes that this is a complex request is coming from
1329
-** a robot if all of the following are true:
1330
-**
1331
-** * The user is "nobody".
1332
-** * Either the REFERER field of the HTTP header is missing or empty,
1333
-** or the USERAGENT field of the HTTP header suggests that
1334
-** the request as coming from a robot.
1335
-** * There are one or more query parameters other than "name".
1336
-**
1337
-** Robot restrictions are governed by settings.
1338
-**
1339
-** robot-restrict The value is a list of GLOB patterns for pages
1340
-** that should restrict robot access. No restrictions
1341
-** are applied if this setting is undefined or is
1342
-** an empty string.
1343
-*/
1344
-void login_restrict_robot_access(void){
1345
- const char *zGlob;
1346
- int isMatch = 1;
1347
- int nQP; /* Number of query parameters other than name= */
1348
- if( g.zLogin!=0 ) return;
1349
- zGlob = db_get("robot-restrict",0);
1350
- if( zGlob==0 || zGlob[0]==0 ) return;
1351
- if( g.isHuman ){
1352
- const char *zReferer;
1353
- const char *zAccept;
1354
- const char *zBr;
1355
- zReferer = P("HTTP_REFERER");
1356
- if( zReferer && zReferer[0]!=0 ) return;
1357
-
1358
- /* Robots typically do not accept the brotli encoding, at least not
1359
- ** at the time of this writing (2025-04-01), but standard web-browser
1360
- ** all generally do accept brotli. So if brotli is accepted,
1361
- ** assume we are not talking to a robot. We might want to revisit this
1362
- ** heuristic in the future...
1363
- */
1364
- if( (zAccept = P("HTTP_ACCEPT_ENCODING"))!=0
1365
- && (zBr = strstr(zAccept,"br"))!=0
1366
- && !fossil_isalnum(zBr[2])
1367
- && (zBr==zAccept || !fossil_isalnum(zBr[-1]))
1368
- ){
1369
- return;
1370
- }
1371
- }
1372
- nQP = cgi_qp_count();
1373
- if( nQP<1 ) return;
1374
- isMatch = glob_multi_match(zGlob, g.zPath);
1375
- if( !isMatch ) return;
1376
-
1377
- /* Check for exceptions to the restriction on the number of query
1378
- ** parameters. */
1379
- zGlob = db_get("robot-restrict-qp",0);
1380
- if( zGlob && zGlob[0] ){
1381
- char *zPath = mprintf("%s/%d", g.zPath, nQP);
1382
- isMatch = glob_multi_match(zGlob, zPath);
1383
- fossil_free(zPath);
1384
- if( isMatch ) return;
1385
- }
1386
-
1387
- /* If we reach this point, it means we have a situation where we
1388
- ** want to restrict the activity of a robot.
1389
- */
1390
- g.isHuman = 0;
1391
- (void)exclude_spiders(0);
1392
- cgi_reply();
1393
- fossil_exit(0);
1394
-}
1395
-
13961308
/*
13971309
** When this routine is called, we know that the request does not
13981310
** have a login on the present repository. This routine checks to
13991311
** see if their login cookie might be for another member of the
14001312
** login-group.
@@ -1429,11 +1341,11 @@
14291341
**
14301342
** g.userUid Database USER.UID value. Might be -1 for "nobody"
14311343
** g.zLogin Database USER.LOGIN value. NULL for user "nobody"
14321344
** g.perm Permissions granted to this user
14331345
** g.anon Permissions that would be available to anonymous
1434
-** g.isHuman True if the user is human, not a spider or robot
1346
+** g.isRobot True if the client is known to be a spider or robot
14351347
** g.perm Populated based on user account's capabilities
14361348
**
14371349
*/
14381350
void login_check_credentials(void){
14391351
int uid = 0; /* User id */
@@ -1470,11 +1382,11 @@
14701382
uid = db_int(0, "SELECT uid FROM user WHERE cap LIKE '%%s%%'");
14711383
}
14721384
g.zLogin = db_text("?", "SELECT login FROM user WHERE uid=%d", uid);
14731385
zCap = "sxy";
14741386
g.noPswd = 1;
1475
- g.isHuman = 1;
1387
+ g.isRobot = 0;
14761388
zSeed = db_text("??", "SELECT uid||quote(login)||quote(pw)||quote(cookie)"
14771389
" FROM user WHERE uid=%d", uid);
14781390
login_create_csrf_secret(zSeed);
14791391
fossil_free(zSeed);
14801392
}
@@ -1604,12 +1516,15 @@
16041516
login_create_csrf_secret("none");
16051517
}
16061518
16071519
login_set_uid(uid, zCap);
16081520
1609
- /* Maybe restrict access to robots */
1610
- login_restrict_robot_access();
1521
+ /* Maybe restrict access by robots */
1522
+ if( g.zLogin==0 && robot_restrict(g.zPath) ){
1523
+ cgi_reply();
1524
+ fossil_exit(0);
1525
+ }
16111526
}
16121527
16131528
/*
16141529
** Set the current logged in user to be uid. zCap is precomputed
16151530
** (override) capabilities. If zCap==0, then look up the capabilities
@@ -1644,15 +1559,15 @@
16441559
g.userUid = uid;
16451560
if( fossil_strcmp(g.zLogin,"nobody")==0 ){
16461561
g.zLogin = 0;
16471562
}
16481563
if( PB("isrobot") ){
1649
- g.isHuman = 0;
1564
+ g.isRobot = 1;
16501565
}else if( g.zLogin==0 ){
1651
- g.isHuman = isHuman(P("HTTP_USER_AGENT"));
1566
+ g.isRobot = !isHuman(P("HTTP_USER_AGENT"));
16521567
}else{
1653
- g.isHuman = 1;
1568
+ g.isRobot = 0;
16541569
}
16551570
16561571
/* Set the capabilities */
16571572
login_replace_capabilities(zCap, 0);
16581573
@@ -1662,11 +1577,11 @@
16621577
** enabled for this repository and make appropriate adjustments to the
16631578
** permission flags if it is. This should be done before the permissions
16641579
** are (potentially) copied to the anonymous permission set; otherwise,
16651580
** those will be out-of-sync.
16661581
*/
1667
- if( zCap[0] && !g.perm.Hyperlink && g.isHuman ){
1582
+ if( zCap[0] && !g.perm.Hyperlink && !g.isRobot ){
16681583
int autoLink = db_get_int("auto-hyperlink",1);
16691584
if( autoLink==1 ){
16701585
g.jsHref = 1;
16711586
g.perm.Hyperlink = 1;
16721587
}else if( autoLink==2 ){
16731588
--- src/login.c
+++ src/login.c
@@ -1303,98 +1303,10 @@
1303 }
1304 fossil_free(zDecode);
1305 return uid;
1306 }
1307
1308 /*
1309 ** SETTING: robot-restrict width=40 block-text
1310 ** The VALUE of this setting is a list of GLOB patterns that match
1311 ** pages for which complex HTTP requests from robots should be disallowed.
1312 ** The recommended value for this setting is:
1313 **
1314 ** timeline,vdiff,fdiff,annotate,blame
1315 **
1316 */
1317
1318 /*
1319 ** Check to see if the current HTTP request is a complex request that
1320 ** is coming from a robot and if access should restricted for such robots.
1321 ** For the purposes of this module, a "complex request" is an HTTP
1322 ** request with one or more query parameters other than "name".
1323 **
1324 ** If this routine determines that robots should be restricted, then
1325 ** this routine publishes a redirect to the honeypot and exits without
1326 ** returning to the caller.
1327 **
1328 ** This routine believes that this is a complex request is coming from
1329 ** a robot if all of the following are true:
1330 **
1331 ** * The user is "nobody".
1332 ** * Either the REFERER field of the HTTP header is missing or empty,
1333 ** or the USERAGENT field of the HTTP header suggests that
1334 ** the request as coming from a robot.
1335 ** * There are one or more query parameters other than "name".
1336 **
1337 ** Robot restrictions are governed by settings.
1338 **
1339 ** robot-restrict The value is a list of GLOB patterns for pages
1340 ** that should restrict robot access. No restrictions
1341 ** are applied if this setting is undefined or is
1342 ** an empty string.
1343 */
1344 void login_restrict_robot_access(void){
1345 const char *zGlob;
1346 int isMatch = 1;
1347 int nQP; /* Number of query parameters other than name= */
1348 if( g.zLogin!=0 ) return;
1349 zGlob = db_get("robot-restrict",0);
1350 if( zGlob==0 || zGlob[0]==0 ) return;
1351 if( g.isHuman ){
1352 const char *zReferer;
1353 const char *zAccept;
1354 const char *zBr;
1355 zReferer = P("HTTP_REFERER");
1356 if( zReferer && zReferer[0]!=0 ) return;
1357
1358 /* Robots typically do not accept the brotli encoding, at least not
1359 ** at the time of this writing (2025-04-01), but standard web-browser
1360 ** all generally do accept brotli. So if brotli is accepted,
1361 ** assume we are not talking to a robot. We might want to revisit this
1362 ** heuristic in the future...
1363 */
1364 if( (zAccept = P("HTTP_ACCEPT_ENCODING"))!=0
1365 && (zBr = strstr(zAccept,"br"))!=0
1366 && !fossil_isalnum(zBr[2])
1367 && (zBr==zAccept || !fossil_isalnum(zBr[-1]))
1368 ){
1369 return;
1370 }
1371 }
1372 nQP = cgi_qp_count();
1373 if( nQP<1 ) return;
1374 isMatch = glob_multi_match(zGlob, g.zPath);
1375 if( !isMatch ) return;
1376
1377 /* Check for exceptions to the restriction on the number of query
1378 ** parameters. */
1379 zGlob = db_get("robot-restrict-qp",0);
1380 if( zGlob && zGlob[0] ){
1381 char *zPath = mprintf("%s/%d", g.zPath, nQP);
1382 isMatch = glob_multi_match(zGlob, zPath);
1383 fossil_free(zPath);
1384 if( isMatch ) return;
1385 }
1386
1387 /* If we reach this point, it means we have a situation where we
1388 ** want to restrict the activity of a robot.
1389 */
1390 g.isHuman = 0;
1391 (void)exclude_spiders(0);
1392 cgi_reply();
1393 fossil_exit(0);
1394 }
1395
1396 /*
1397 ** When this routine is called, we know that the request does not
1398 ** have a login on the present repository. This routine checks to
1399 ** see if their login cookie might be for another member of the
1400 ** login-group.
@@ -1429,11 +1341,11 @@
1429 **
1430 ** g.userUid Database USER.UID value. Might be -1 for "nobody"
1431 ** g.zLogin Database USER.LOGIN value. NULL for user "nobody"
1432 ** g.perm Permissions granted to this user
1433 ** g.anon Permissions that would be available to anonymous
1434 ** g.isHuman True if the user is human, not a spider or robot
1435 ** g.perm Populated based on user account's capabilities
1436 **
1437 */
1438 void login_check_credentials(void){
1439 int uid = 0; /* User id */
@@ -1470,11 +1382,11 @@
1470 uid = db_int(0, "SELECT uid FROM user WHERE cap LIKE '%%s%%'");
1471 }
1472 g.zLogin = db_text("?", "SELECT login FROM user WHERE uid=%d", uid);
1473 zCap = "sxy";
1474 g.noPswd = 1;
1475 g.isHuman = 1;
1476 zSeed = db_text("??", "SELECT uid||quote(login)||quote(pw)||quote(cookie)"
1477 " FROM user WHERE uid=%d", uid);
1478 login_create_csrf_secret(zSeed);
1479 fossil_free(zSeed);
1480 }
@@ -1604,12 +1516,15 @@
1604 login_create_csrf_secret("none");
1605 }
1606
1607 login_set_uid(uid, zCap);
1608
1609 /* Maybe restrict access to robots */
1610 login_restrict_robot_access();
 
 
 
1611 }
1612
1613 /*
1614 ** Set the current logged in user to be uid. zCap is precomputed
1615 ** (override) capabilities. If zCap==0, then look up the capabilities
@@ -1644,15 +1559,15 @@
1644 g.userUid = uid;
1645 if( fossil_strcmp(g.zLogin,"nobody")==0 ){
1646 g.zLogin = 0;
1647 }
1648 if( PB("isrobot") ){
1649 g.isHuman = 0;
1650 }else if( g.zLogin==0 ){
1651 g.isHuman = isHuman(P("HTTP_USER_AGENT"));
1652 }else{
1653 g.isHuman = 1;
1654 }
1655
1656 /* Set the capabilities */
1657 login_replace_capabilities(zCap, 0);
1658
@@ -1662,11 +1577,11 @@
1662 ** enabled for this repository and make appropriate adjustments to the
1663 ** permission flags if it is. This should be done before the permissions
1664 ** are (potentially) copied to the anonymous permission set; otherwise,
1665 ** those will be out-of-sync.
1666 */
1667 if( zCap[0] && !g.perm.Hyperlink && g.isHuman ){
1668 int autoLink = db_get_int("auto-hyperlink",1);
1669 if( autoLink==1 ){
1670 g.jsHref = 1;
1671 g.perm.Hyperlink = 1;
1672 }else if( autoLink==2 ){
1673
--- src/login.c
+++ src/login.c
@@ -1303,98 +1303,10 @@
1303 }
1304 fossil_free(zDecode);
1305 return uid;
1306 }
1307
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1308 /*
1309 ** When this routine is called, we know that the request does not
1310 ** have a login on the present repository. This routine checks to
1311 ** see if their login cookie might be for another member of the
1312 ** login-group.
@@ -1429,11 +1341,11 @@
1341 **
1342 ** g.userUid Database USER.UID value. Might be -1 for "nobody"
1343 ** g.zLogin Database USER.LOGIN value. NULL for user "nobody"
1344 ** g.perm Permissions granted to this user
1345 ** g.anon Permissions that would be available to anonymous
1346 ** g.isRobot True if the client is known to be a spider or robot
1347 ** g.perm Populated based on user account's capabilities
1348 **
1349 */
1350 void login_check_credentials(void){
1351 int uid = 0; /* User id */
@@ -1470,11 +1382,11 @@
1382 uid = db_int(0, "SELECT uid FROM user WHERE cap LIKE '%%s%%'");
1383 }
1384 g.zLogin = db_text("?", "SELECT login FROM user WHERE uid=%d", uid);
1385 zCap = "sxy";
1386 g.noPswd = 1;
1387 g.isRobot = 0;
1388 zSeed = db_text("??", "SELECT uid||quote(login)||quote(pw)||quote(cookie)"
1389 " FROM user WHERE uid=%d", uid);
1390 login_create_csrf_secret(zSeed);
1391 fossil_free(zSeed);
1392 }
@@ -1604,12 +1516,15 @@
1516 login_create_csrf_secret("none");
1517 }
1518
1519 login_set_uid(uid, zCap);
1520
1521 /* Maybe restrict access by robots */
1522 if( g.zLogin==0 && robot_restrict(g.zPath) ){
1523 cgi_reply();
1524 fossil_exit(0);
1525 }
1526 }
1527
1528 /*
1529 ** Set the current logged in user to be uid. zCap is precomputed
1530 ** (override) capabilities. If zCap==0, then look up the capabilities
@@ -1644,15 +1559,15 @@
1559 g.userUid = uid;
1560 if( fossil_strcmp(g.zLogin,"nobody")==0 ){
1561 g.zLogin = 0;
1562 }
1563 if( PB("isrobot") ){
1564 g.isRobot = 1;
1565 }else if( g.zLogin==0 ){
1566 g.isRobot = !isHuman(P("HTTP_USER_AGENT"));
1567 }else{
1568 g.isRobot = 0;
1569 }
1570
1571 /* Set the capabilities */
1572 login_replace_capabilities(zCap, 0);
1573
@@ -1662,11 +1577,11 @@
1577 ** enabled for this repository and make appropriate adjustments to the
1578 ** permission flags if it is. This should be done before the permissions
1579 ** are (potentially) copied to the anonymous permission set; otherwise,
1580 ** those will be out-of-sync.
1581 */
1582 if( zCap[0] && !g.perm.Hyperlink && !g.isRobot ){
1583 int autoLink = db_get_int("auto-hyperlink",1);
1584 if( autoLink==1 ){
1585 g.jsHref = 1;
1586 g.perm.Hyperlink = 1;
1587 }else if( autoLink==2 ){
1588
+2 -1
--- src/main.c
+++ src/main.c
@@ -233,11 +233,12 @@
233233
* applicable when using SEE on Windows or Linux. */
234234
#endif
235235
int useLocalauth; /* No login required if from 127.0.0.1 */
236236
int noPswd; /* Logged in without password (on 127.0.0.1) */
237237
int userUid; /* Integer user id */
238
- int isHuman; /* True if access by a human, not a spider or bot */
238
+ int isRobot; /* True if the client is definitely a robot. False
239
+ ** negatives are common for this flag */
239240
int comFmtFlags; /* Zero or more "COMMENT_PRINT_*" bit flags, should be
240241
** accessed through get_comment_format(). */
241242
const char *zSockName; /* Name of the unix-domain socket file */
242243
const char *zSockMode; /* File permissions for unix-domain socket */
243244
const char *zSockOwner; /* Owner, or owner:group for unix-domain socket */
244245
--- src/main.c
+++ src/main.c
@@ -233,11 +233,12 @@
233 * applicable when using SEE on Windows or Linux. */
234 #endif
235 int useLocalauth; /* No login required if from 127.0.0.1 */
236 int noPswd; /* Logged in without password (on 127.0.0.1) */
237 int userUid; /* Integer user id */
238 int isHuman; /* True if access by a human, not a spider or bot */
 
239 int comFmtFlags; /* Zero or more "COMMENT_PRINT_*" bit flags, should be
240 ** accessed through get_comment_format(). */
241 const char *zSockName; /* Name of the unix-domain socket file */
242 const char *zSockMode; /* File permissions for unix-domain socket */
243 const char *zSockOwner; /* Owner, or owner:group for unix-domain socket */
244
--- src/main.c
+++ src/main.c
@@ -233,11 +233,12 @@
233 * applicable when using SEE on Windows or Linux. */
234 #endif
235 int useLocalauth; /* No login required if from 127.0.0.1 */
236 int noPswd; /* Logged in without password (on 127.0.0.1) */
237 int userUid; /* Integer user id */
238 int isRobot; /* True if the client is definitely a robot. False
239 ** negatives are common for this flag */
240 int comFmtFlags; /* Zero or more "COMMENT_PRINT_*" bit flags, should be
241 ** accessed through get_comment_format(). */
242 const char *zSockName; /* Name of the unix-domain socket file */
243 const char *zSockMode; /* File permissions for unix-domain socket */
244 const char *zSockOwner; /* Owner, or owner:group for unix-domain socket */
245
+109 -60
--- src/robot.c
+++ src/robot.c
@@ -22,19 +22,11 @@
2222
#include "config.h"
2323
#include "robot.h"
2424
#include <assert.h>
2525
#include <time.h>
2626
27
-/*
28
-** SETTING: robot-squelch width=10 default=200
29
-** The VALUE of is an integer between 0 and 1000 that determines how
30
-** readily Fossil will squelch requests from robots. A value of 0
31
-** means "never squelch requests". A value of 1000 means "always
32
-** squelch requests from user 'nobody'". For values greater than 0
33
-** and less than 1000, the decision to squelch is based on a variety
34
-** of heuristics, but is more likely to occur the larger the number.
35
-*/
27
+#define POW_COOKIE "fossil-proofofwork"
3628
3729
/*
3830
** Rewrite the current page with a robot squelch captcha and return 1.
3931
**
4032
** Or, if valid proof-of-work is present as either a query parameter or
@@ -71,14 +63,14 @@
7163
h2 = (h2 % 900000000) + 100000000;
7264
7365
/* If there is already a proof-of-work cookie with this value
7466
** that means that the user agent has already authenticated.
7567
*/
76
- z = P("fossil-proofofwork");
68
+ z = P(POW_COOKIE);
7769
if( z
7870
&& (atoi(z)==h1 || atoi(z)==h2)
79
- && !cgi_is_qp("fossil-proofofwork") ){
71
+ && !cgi_is_qp(POW_COOKIE) ){
8072
return 0;
8173
}
8274
8375
/* Check for a proof query parameter. If found, that means that
8476
** the captcha has just now passed, so set the proof-of-work cookie
@@ -86,83 +78,140 @@
8678
*/
8779
z = P("proof");
8880
if( z
8981
&& (atoi(z)==h1 || atoi(z)==h2)
9082
){
91
- cgi_set_cookie("fossil-proofofwork",z,"/",900);
83
+ cgi_set_cookie(POW_COOKIE,z,"/",900);
9284
return 0;
9385
}
9486
cgi_tag_query_parameter("proof");
9587
9688
/* Ask the client to present proof-of-work */
9789
cgi_reset_content();
9890
cgi_set_content_type("text/html");
99
- style_header("Captcha");
100
- @ <h1>Prove That You Are Human</h1>
91
+ style_header("Browser Verification");
92
+ @ <h1 id="x1">Checking to see if you are a robot<span id="x2"></span></h1>
10193
@ <form method="GET">
102
- @ <p>Press the button below</p><p>
94
+ @ <p id="x3" style="visibility:hidden;">\
95
+ @ Press <input type="submit" id="x5" value="Ok" focus> to continue</p>
10396
cgi_query_parameters_to_hidden();
104
- @ <input id="vx" type="hidden" name="proof" value="0">
105
- @ <input id="cx" type="submit" value="Wait..." disabled>
97
+ @ <input id="x4" type="hidden" name="proof" value="0">
10698
@ </form>
10799
@ <script nonce='%s(style_nonce())'>
108
- @ function Nhtot1520(x){return document.getElementById(x);}
109
- @ function Aoxlxzajv(h){\
110
- @ Nhtot1520("vx").value=h;\
111
- @ Nhtot1520("cx").value="Ok";\
112
- @ Nhtot1520("cx").disabled=false;\
113
- @ }
114
- @ function Vhcnyarsm(h,a){\
115
- @ if(a>0){setTimeout(Vhcnyarsm,1,h+a,a-1);}else{Aoxlxzajv(h);}\
116
- @ }
117
- k = 200 + h2%99;
100
+ @ function aaa(x){return document.getElementById(x);}
101
+ @ function bbb(h,a){
102
+ @ aaa("x4").value=h
103
+ @ if((a%%75)==0){
104
+ @ aaa("x2").textContent=aaa("x2").textContent+".";
105
+ @ }
106
+ @ if(a>0){
107
+ @ setTimeout(bbb,1,h+a,a-1);
108
+ @ }else{
109
+ @ aaa("x3").style.visibility="visible";
110
+ @ aaa("x2").textContent="";
111
+ @ aaa("x1").textContent="All clear";
112
+ @ aaa("x5").focus();
113
+ @ }
114
+ @ }
115
+ k = 800 + h2%99;
118116
h2 = (k*k + k)/2;
119
- @ setTimeout(function(){Vhcnyarsm(%u(h1-h2),%u(k));},10);
117
+ @ setTimeout(function(){bbb(%u(h1-h2),%u(k));},10);
120118
@ </script>
121119
style_finish_page();
122120
return 1;
123121
}
124122
123
+/*
124
+** SETTING: robot-restrict width=40 block-text
125
+** The VALUE of this setting is a list of GLOB patterns that match
126
+** pages for which complex HTTP requests from unauthenicated clients
127
+** should be disallowed. "Unauthenticated" means the user is "nobody".
128
+** The recommended value for this setting is:
129
+**
130
+** timelineX,diff,annotate,zip,fileage,file
131
+**
132
+** The "diff" tag covers all diffing pages such as /vdiff, /fdiff, and
133
+** /vpatch. The "annotate" tag also covers /blame and /praise. "zip"
134
+** also covers /tarball and /sqlar. If a tag has an "X" character appended,
135
+** then it only applies if query parameters are such that the page is
136
+** particularly difficult to compute.
137
+**
138
+** In all other case, the tag should exactly match the page name.
139
+*/
125140
126141
/*
127
-** WEBPAGE functions can invoke this routine with an argument
128
-** that is between 0 and 1000. Based on that argument, and on
129
-** other factors, this routine decides whether or not to squelch
130
-** the request. "Squelch" in this context, means to require the
131
-** client to show proof-of-work before the request is processed.
132
-** The idea here is to prevent server overload due to excess robot
133
-** traffic. If a robot (or any client application really) wants us
134
-** to spend a lot of CPU computing some result for it, then it needs
135
-** to first demonstrate good faith by doing some make-work for us.
136
-**
137
-** This routine returns true for a squelch and false if the original
138
-** request should go through.
139
-**
140
-** The input parameter is an estimate of how much CPU time
141
-** and bandwidth is needed to compute a response. The higher the
142
-** value of this parameter, the more likely this routine is to squelch
143
-** the page. A value of zero means "never squelch". A value of
144
-** 1000 means always squelch if the user is "nobody".
145
-**
146
-** Squelching only happens if the user is "nobody". If the request
147
-** comes from any other user, including user "anonymous", the request
148
-** is never squelched.
149
-*/
150
-int robot_squelch(int n){
151
- const char *zToken;
152
- int iSquelch;
153
- assert( n>=0 && n<=1000 );
154
- if( g.zLogin ) return 0; /* Logged in users always get through */
155
- if( n==0 ) return 0; /* Squelch is completely disabled */
142
+** Return the default restriction GLOB
143
+*/
144
+const char *robot_restrict_default(void){
145
+ return "timelineX,diff,annotate,zip,fileage,file";
146
+}
147
+/*
148
+** Check to see if the page named in the argument is on the
149
+** robot-restrict list. If it is on the list and if the user
150
+** is "nobody" then bring up a captcha to test to make sure that
151
+** client is not a robot.
152
+**
153
+** This routine returns true if a captcha was rendered and if subsequent
154
+** page generation should be aborted. It returns false if the page
155
+** should not be restricted and should be rendered normally.
156
+*/
157
+int robot_restrict(const char *zPage){
158
+ const char *zGlob;
159
+ const char *zToken;
160
+ if( g.zLogin ) return 0; /* Logged in users always get through */
161
+ zGlob = db_get("robot-restrict",robot_restrict_default());
162
+ if( zGlob==0 || zGlob[0]==0 ) return 0;
163
+ if( !glob_multi_match(zGlob, zPage) ) return 0;
156164
zToken = P("token");
157165
if( zToken!=0
158166
&& db_exists("SELECT 1 FROM config WHERE name='token-%q'", zToken)
159167
){
160168
return 0; /* There is a valid token= query parameter */
161169
}
162
- iSquelch = db_get_int("robot-squelch",200);
163
- if( iSquelch<=0 ) return 0;
164
- if( n+iSquelch>=1000 && robot_proofofwork() ){
170
+ if( robot_proofofwork() ){
165171
return 1;
166172
}
167173
return 0;
168174
}
175
+
176
+
177
+/*
178
+** WEBPAGE: test-robotck
179
+**
180
+** Run the robot_restrict() function using the value of the "name="
181
+** query parameter as an argument. Used for testing the robot_restrict()
182
+** logic.
183
+**
184
+** Whenever this page is successfully rendered (when it doesn't go to
185
+** the captcha) it deletes the proof-of-work cookie. So reloading the
186
+** page will reset the cookie and restart the verification.
187
+*/
188
+void robot_restrict_test_page(void){
189
+ const char *zName = P("name");
190
+ const char *zP1 = P("proof");
191
+ const char *zP2 = P(POW_COOKIE);
192
+ const char *z;
193
+ if( zName==0 || zName[0]==0 ) zName = g.zPath;
194
+ login_check_credentials();
195
+ if( !g.perm.Admin ){ login_needed(0); return; }
196
+ g.zLogin = 0;
197
+ if( robot_restrict(zName) ) return;
198
+ style_set_current_feature("test");
199
+ style_header("robot_restrict() test");
200
+ @ <h1>Captcha passed</h1>
201
+ @
202
+ @ <p>
203
+ if( zP1 && zP1[0] ){
204
+ @ proof=%h(zP1)<br>
205
+ }
206
+ if( zP2 && zP2[0] ){
207
+ @ fossil_proofofwork=%h(zP2)<br>
208
+ cgi_set_cookie(POW_COOKIE,"",0,-1);
209
+ }
210
+ z = db_get("robot-restrict",robot_restrict_default());
211
+ if( z && z[0] ){
212
+ @ robot-restrict=%h(z)</br>
213
+ }
214
+ @ </p>
215
+ @ <p><a href="%R/test-robotck/%h(zName)">Retry</a>
216
+ style_finish_page();
217
+}
169218
--- src/robot.c
+++ src/robot.c
@@ -22,19 +22,11 @@
22 #include "config.h"
23 #include "robot.h"
24 #include <assert.h>
25 #include <time.h>
26
27 /*
28 ** SETTING: robot-squelch width=10 default=200
29 ** The VALUE of is an integer between 0 and 1000 that determines how
30 ** readily Fossil will squelch requests from robots. A value of 0
31 ** means "never squelch requests". A value of 1000 means "always
32 ** squelch requests from user 'nobody'". For values greater than 0
33 ** and less than 1000, the decision to squelch is based on a variety
34 ** of heuristics, but is more likely to occur the larger the number.
35 */
36
37 /*
38 ** Rewrite the current page with a robot squelch captcha and return 1.
39 **
40 ** Or, if valid proof-of-work is present as either a query parameter or
@@ -71,14 +63,14 @@
71 h2 = (h2 % 900000000) + 100000000;
72
73 /* If there is already a proof-of-work cookie with this value
74 ** that means that the user agent has already authenticated.
75 */
76 z = P("fossil-proofofwork");
77 if( z
78 && (atoi(z)==h1 || atoi(z)==h2)
79 && !cgi_is_qp("fossil-proofofwork") ){
80 return 0;
81 }
82
83 /* Check for a proof query parameter. If found, that means that
84 ** the captcha has just now passed, so set the proof-of-work cookie
@@ -86,83 +78,140 @@
86 */
87 z = P("proof");
88 if( z
89 && (atoi(z)==h1 || atoi(z)==h2)
90 ){
91 cgi_set_cookie("fossil-proofofwork",z,"/",900);
92 return 0;
93 }
94 cgi_tag_query_parameter("proof");
95
96 /* Ask the client to present proof-of-work */
97 cgi_reset_content();
98 cgi_set_content_type("text/html");
99 style_header("Captcha");
100 @ <h1>Prove That You Are Human</h1>
101 @ <form method="GET">
102 @ <p>Press the button below</p><p>
 
103 cgi_query_parameters_to_hidden();
104 @ <input id="vx" type="hidden" name="proof" value="0">
105 @ <input id="cx" type="submit" value="Wait..." disabled>
106 @ </form>
107 @ <script nonce='%s(style_nonce())'>
108 @ function Nhtot1520(x){return document.getElementById(x);}
109 @ function Aoxlxzajv(h){\
110 @ Nhtot1520("vx").value=h;\
111 @ Nhtot1520("cx").value="Ok";\
112 @ Nhtot1520("cx").disabled=false;\
113 @ }
114 @ function Vhcnyarsm(h,a){\
115 @ if(a>0){setTimeout(Vhcnyarsm,1,h+a,a-1);}else{Aoxlxzajv(h);}\
116 @ }
117 k = 200 + h2%99;
 
 
 
 
 
 
118 h2 = (k*k + k)/2;
119 @ setTimeout(function(){Vhcnyarsm(%u(h1-h2),%u(k));},10);
120 @ </script>
121 style_finish_page();
122 return 1;
123 }
124
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
125
126 /*
127 ** WEBPAGE functions can invoke this routine with an argument
128 ** that is between 0 and 1000. Based on that argument, and on
129 ** other factors, this routine decides whether or not to squelch
130 ** the request. "Squelch" in this context, means to require the
131 ** client to show proof-of-work before the request is processed.
132 ** The idea here is to prevent server overload due to excess robot
133 ** traffic. If a robot (or any client application really) wants us
134 ** to spend a lot of CPU computing some result for it, then it needs
135 ** to first demonstrate good faith by doing some make-work for us.
136 **
137 ** This routine returns true for a squelch and false if the original
138 ** request should go through.
139 **
140 ** The input parameter is an estimate of how much CPU time
141 ** and bandwidth is needed to compute a response. The higher the
142 ** value of this parameter, the more likely this routine is to squelch
143 ** the page. A value of zero means "never squelch". A value of
144 ** 1000 means always squelch if the user is "nobody".
145 **
146 ** Squelching only happens if the user is "nobody". If the request
147 ** comes from any other user, including user "anonymous", the request
148 ** is never squelched.
149 */
150 int robot_squelch(int n){
151 const char *zToken;
152 int iSquelch;
153 assert( n>=0 && n<=1000 );
154 if( g.zLogin ) return 0; /* Logged in users always get through */
155 if( n==0 ) return 0; /* Squelch is completely disabled */
156 zToken = P("token");
157 if( zToken!=0
158 && db_exists("SELECT 1 FROM config WHERE name='token-%q'", zToken)
159 ){
160 return 0; /* There is a valid token= query parameter */
161 }
162 iSquelch = db_get_int("robot-squelch",200);
163 if( iSquelch<=0 ) return 0;
164 if( n+iSquelch>=1000 && robot_proofofwork() ){
165 return 1;
166 }
167 return 0;
168 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
169
--- src/robot.c
+++ src/robot.c
@@ -22,19 +22,11 @@
22 #include "config.h"
23 #include "robot.h"
24 #include <assert.h>
25 #include <time.h>
26
27 #define POW_COOKIE "fossil-proofofwork"
 
 
 
 
 
 
 
 
28
29 /*
30 ** Rewrite the current page with a robot squelch captcha and return 1.
31 **
32 ** Or, if valid proof-of-work is present as either a query parameter or
@@ -71,14 +63,14 @@
63 h2 = (h2 % 900000000) + 100000000;
64
65 /* If there is already a proof-of-work cookie with this value
66 ** that means that the user agent has already authenticated.
67 */
68 z = P(POW_COOKIE);
69 if( z
70 && (atoi(z)==h1 || atoi(z)==h2)
71 && !cgi_is_qp(POW_COOKIE) ){
72 return 0;
73 }
74
75 /* Check for a proof query parameter. If found, that means that
76 ** the captcha has just now passed, so set the proof-of-work cookie
@@ -86,83 +78,140 @@
78 */
79 z = P("proof");
80 if( z
81 && (atoi(z)==h1 || atoi(z)==h2)
82 ){
83 cgi_set_cookie(POW_COOKIE,z,"/",900);
84 return 0;
85 }
86 cgi_tag_query_parameter("proof");
87
88 /* Ask the client to present proof-of-work */
89 cgi_reset_content();
90 cgi_set_content_type("text/html");
91 style_header("Browser Verification");
92 @ <h1 id="x1">Checking to see if you are a robot<span id="x2"></span></h1>
93 @ <form method="GET">
94 @ <p id="x3" style="visibility:hidden;">\
95 @ Press <input type="submit" id="x5" value="Ok" focus> to continue</p>
96 cgi_query_parameters_to_hidden();
97 @ <input id="x4" type="hidden" name="proof" value="0">
 
98 @ </form>
99 @ <script nonce='%s(style_nonce())'>
100 @ function aaa(x){return document.getElementById(x);}
101 @ function bbb(h,a){
102 @ aaa("x4").value=h
103 @ if((a%%75)==0){
104 @ aaa("x2").textContent=aaa("x2").textContent+".";
105 @ }
106 @ if(a>0){
107 @ setTimeout(bbb,1,h+a,a-1);
108 @ }else{
109 @ aaa("x3").style.visibility="visible";
110 @ aaa("x2").textContent="";
111 @ aaa("x1").textContent="All clear";
112 @ aaa("x5").focus();
113 @ }
114 @ }
115 k = 800 + h2%99;
116 h2 = (k*k + k)/2;
117 @ setTimeout(function(){bbb(%u(h1-h2),%u(k));},10);
118 @ </script>
119 style_finish_page();
120 return 1;
121 }
122
123 /*
124 ** SETTING: robot-restrict width=40 block-text
125 ** The VALUE of this setting is a list of GLOB patterns that match
126 ** pages for which complex HTTP requests from unauthenicated clients
127 ** should be disallowed. "Unauthenticated" means the user is "nobody".
128 ** The recommended value for this setting is:
129 **
130 ** timelineX,diff,annotate,zip,fileage,file
131 **
132 ** The "diff" tag covers all diffing pages such as /vdiff, /fdiff, and
133 ** /vpatch. The "annotate" tag also covers /blame and /praise. "zip"
134 ** also covers /tarball and /sqlar. If a tag has an "X" character appended,
135 ** then it only applies if query parameters are such that the page is
136 ** particularly difficult to compute.
137 **
138 ** In all other case, the tag should exactly match the page name.
139 */
140
141 /*
142 ** Return the default restriction GLOB
143 */
144 const char *robot_restrict_default(void){
145 return "timelineX,diff,annotate,zip,fileage,file";
146 }
147 /*
148 ** Check to see if the page named in the argument is on the
149 ** robot-restrict list. If it is on the list and if the user
150 ** is "nobody" then bring up a captcha to test to make sure that
151 ** client is not a robot.
152 **
153 ** This routine returns true if a captcha was rendered and if subsequent
154 ** page generation should be aborted. It returns false if the page
155 ** should not be restricted and should be rendered normally.
156 */
157 int robot_restrict(const char *zPage){
158 const char *zGlob;
159 const char *zToken;
160 if( g.zLogin ) return 0; /* Logged in users always get through */
161 zGlob = db_get("robot-restrict",robot_restrict_default());
162 if( zGlob==0 || zGlob[0]==0 ) return 0;
163 if( !glob_multi_match(zGlob, zPage) ) return 0;
 
 
 
 
 
 
 
164 zToken = P("token");
165 if( zToken!=0
166 && db_exists("SELECT 1 FROM config WHERE name='token-%q'", zToken)
167 ){
168 return 0; /* There is a valid token= query parameter */
169 }
170 if( robot_proofofwork() ){
 
 
171 return 1;
172 }
173 return 0;
174 }
175
176
177 /*
178 ** WEBPAGE: test-robotck
179 **
180 ** Run the robot_restrict() function using the value of the "name="
181 ** query parameter as an argument. Used for testing the robot_restrict()
182 ** logic.
183 **
184 ** Whenever this page is successfully rendered (when it doesn't go to
185 ** the captcha) it deletes the proof-of-work cookie. So reloading the
186 ** page will reset the cookie and restart the verification.
187 */
188 void robot_restrict_test_page(void){
189 const char *zName = P("name");
190 const char *zP1 = P("proof");
191 const char *zP2 = P(POW_COOKIE);
192 const char *z;
193 if( zName==0 || zName[0]==0 ) zName = g.zPath;
194 login_check_credentials();
195 if( !g.perm.Admin ){ login_needed(0); return; }
196 g.zLogin = 0;
197 if( robot_restrict(zName) ) return;
198 style_set_current_feature("test");
199 style_header("robot_restrict() test");
200 @ <h1>Captcha passed</h1>
201 @
202 @ <p>
203 if( zP1 && zP1[0] ){
204 @ proof=%h(zP1)<br>
205 }
206 if( zP2 && zP2[0] ){
207 @ fossil_proofofwork=%h(zP2)<br>
208 cgi_set_cookie(POW_COOKIE,"",0,-1);
209 }
210 z = db_get("robot-restrict",robot_restrict_default());
211 if( z && z[0] ){
212 @ robot-restrict=%h(z)</br>
213 }
214 @ </p>
215 @ <p><a href="%R/test-robotck/%h(zName)">Retry</a>
216 style_finish_page();
217 }
218
+20 -37
--- src/setup.c
+++ src/setup.c
@@ -495,20 +495,29 @@
495495
@
496496
@ <form action="%R/setup_robot" method="post"><div>
497497
login_insert_csrf_secret();
498498
@ <input type="submit" name="submit" value="Apply Changes"></p>
499499
@ <hr>
500
- entry_attribute("Robot Squelch", 6, "robot-squelch", "rsq", "200", 0);
501
- @ <p>The "squelch" setting determines how aggressive Fossil is about
502
- @ trying to weed out robots using captchas. Squelch only applies to
503
- @ expensive requests from user "nobody". The higher the squelch setting,
504
- @ the more likely the request is to generate a captcha instead of the
505
- @ requested page. Squelch can be any integer between 0 and 1000.
506
- @ 0 means squelch is disabled and all requests go through without a
507
- @ captcha. 1000 means every expensive request from user "nobody" gets
508
- @ a captcha.
509
- @ (Property: "robot-squelch")</p>
500
+ @ <p><b>Do not allow robots access to these pages.</b>
501
+ @ <p> If the page name matches the GLOB pattern of this setting, and the
502
+ @ users is "nobody", and the client has not previously passed a captcha
503
+ @ test to show that it is not a robot, then the page is not displayed.
504
+ @ A captcha test is is rendered instead.
505
+ @ The recommended value for this setting is:
506
+ @ <p>
507
+ @ &emsp;&emsp;&emsp;<tt>%h(robot_restrict_default())</tt>
508
+ @ <p>
509
+ @ The "diff" tag covers all diffing pages such as /vdiff, /fdiff, and
510
+ @ /vpatch. The "annotate" tag covers /annotate and also /blame and
511
+ @ /praise. The "zip" covers itself and also /tarball and /sqlar. If a
512
+ @ tag has an "X" character appended, then it only applies if query
513
+ @ parameters are such that the page is particularly difficult to compute.
514
+ @ In all other case, the tag should exactly match the page name.
515
+ @ (Property: robot-restrict)
516
+ @ <br>
517
+ textarea_attribute("", 2, 80,
518
+ "robot-restrict", "rbrestrict", robot_restrict_default(), 0);
510519
511520
@ <hr>
512521
addAutoHyperlinkSettings();
513522
514523
@ <hr>
@@ -520,37 +529,11 @@
520529
@ computations here. Set this to 0.0 to disable the load average limit.
521530
@ This limit is only enforced on Unix servers. On Linux systems,
522531
@ access to the /proc virtual filesystem is required, which means this limit
523532
@ might not work inside a chroot() jail.
524533
@ (Property: "max-loadavg")</p>
525
-
526
- @ <hr>
527
- @ <p><b>Do not allow robots to make complex requests
528
- @ against the following pages.</b>
529
- @ <p> A "complex request" is an HTTP request that has one or more query
530
- @ parameters. Some robots will spend hours juggling around query parameters
531
- @ or even forging fake query parameters in an effort to discover new
532
- @ behavior or to find an SQL injection opportunity or similar. This can
533
- @ waste hours of CPU time and gigabytes of bandwidth on the server. A
534
- @ suggested value for this setting is:
535
- @ "<tt>timeline,*diff,vpatch,annotate,blame,praise,dir,tree</tt>".
536
- @ (Property: robot-restrict)
537
- @ <br>
538
- textarea_attribute("", 2, 80,
539
- "robot-restrict", "rbrestrict", "", 0);
540
- @ <br> The following comma-separated GLOB pattern allows for exceptions
541
- @ in the maximum number of query parameters before a request is considered
542
- @ complex. If this GLOB pattern exists and is non-empty and if it
543
- @ matches against the pagename followed by "/" and the number of query
544
- @ parameters, then the request is allowed through. For example, the
545
- @ suggested pattern of "timeline/[012]" allows the /timeline page to
546
- @ pass with up to 2 query parameters besides "name".
547
- @ (Property: robot-restrict-qp)
548
- @ <br>
549
- textarea_attribute("", 2, 80,
550
- "robot-restrict-qp", "rbrestrictqp", "", 0);
551
-
534
+ @
552535
@ <hr>
553536
@ <p><input type="submit" name="submit" value="Apply Changes"></p>
554537
@ </div></form>
555538
db_end_transaction(0);
556539
style_finish_page();
557540
--- src/setup.c
+++ src/setup.c
@@ -495,20 +495,29 @@
495 @
496 @ <form action="%R/setup_robot" method="post"><div>
497 login_insert_csrf_secret();
498 @ <input type="submit" name="submit" value="Apply Changes"></p>
499 @ <hr>
500 entry_attribute("Robot Squelch", 6, "robot-squelch", "rsq", "200", 0);
501 @ <p>The "squelch" setting determines how aggressive Fossil is about
502 @ trying to weed out robots using captchas. Squelch only applies to
503 @ expensive requests from user "nobody". The higher the squelch setting,
504 @ the more likely the request is to generate a captcha instead of the
505 @ requested page. Squelch can be any integer between 0 and 1000.
506 @ 0 means squelch is disabled and all requests go through without a
507 @ captcha. 1000 means every expensive request from user "nobody" gets
508 @ a captcha.
509 @ (Property: "robot-squelch")</p>
 
 
 
 
 
 
 
 
 
510
511 @ <hr>
512 addAutoHyperlinkSettings();
513
514 @ <hr>
@@ -520,37 +529,11 @@
520 @ computations here. Set this to 0.0 to disable the load average limit.
521 @ This limit is only enforced on Unix servers. On Linux systems,
522 @ access to the /proc virtual filesystem is required, which means this limit
523 @ might not work inside a chroot() jail.
524 @ (Property: "max-loadavg")</p>
525
526 @ <hr>
527 @ <p><b>Do not allow robots to make complex requests
528 @ against the following pages.</b>
529 @ <p> A "complex request" is an HTTP request that has one or more query
530 @ parameters. Some robots will spend hours juggling around query parameters
531 @ or even forging fake query parameters in an effort to discover new
532 @ behavior or to find an SQL injection opportunity or similar. This can
533 @ waste hours of CPU time and gigabytes of bandwidth on the server. A
534 @ suggested value for this setting is:
535 @ "<tt>timeline,*diff,vpatch,annotate,blame,praise,dir,tree</tt>".
536 @ (Property: robot-restrict)
537 @ <br>
538 textarea_attribute("", 2, 80,
539 "robot-restrict", "rbrestrict", "", 0);
540 @ <br> The following comma-separated GLOB pattern allows for exceptions
541 @ in the maximum number of query parameters before a request is considered
542 @ complex. If this GLOB pattern exists and is non-empty and if it
543 @ matches against the pagename followed by "/" and the number of query
544 @ parameters, then the request is allowed through. For example, the
545 @ suggested pattern of "timeline/[012]" allows the /timeline page to
546 @ pass with up to 2 query parameters besides "name".
547 @ (Property: robot-restrict-qp)
548 @ <br>
549 textarea_attribute("", 2, 80,
550 "robot-restrict-qp", "rbrestrictqp", "", 0);
551
552 @ <hr>
553 @ <p><input type="submit" name="submit" value="Apply Changes"></p>
554 @ </div></form>
555 db_end_transaction(0);
556 style_finish_page();
557
--- src/setup.c
+++ src/setup.c
@@ -495,20 +495,29 @@
495 @
496 @ <form action="%R/setup_robot" method="post"><div>
497 login_insert_csrf_secret();
498 @ <input type="submit" name="submit" value="Apply Changes"></p>
499 @ <hr>
500 @ <p><b>Do not allow robots access to these pages.</b>
501 @ <p> If the page name matches the GLOB pattern of this setting, and the
502 @ users is "nobody", and the client has not previously passed a captcha
503 @ test to show that it is not a robot, then the page is not displayed.
504 @ A captcha test is is rendered instead.
505 @ The recommended value for this setting is:
506 @ <p>
507 @ &emsp;&emsp;&emsp;<tt>%h(robot_restrict_default())</tt>
508 @ <p>
509 @ The "diff" tag covers all diffing pages such as /vdiff, /fdiff, and
510 @ /vpatch. The "annotate" tag covers /annotate and also /blame and
511 @ /praise. The "zip" covers itself and also /tarball and /sqlar. If a
512 @ tag has an "X" character appended, then it only applies if query
513 @ parameters are such that the page is particularly difficult to compute.
514 @ In all other case, the tag should exactly match the page name.
515 @ (Property: robot-restrict)
516 @ <br>
517 textarea_attribute("", 2, 80,
518 "robot-restrict", "rbrestrict", robot_restrict_default(), 0);
519
520 @ <hr>
521 addAutoHyperlinkSettings();
522
523 @ <hr>
@@ -520,37 +529,11 @@
529 @ computations here. Set this to 0.0 to disable the load average limit.
530 @ This limit is only enforced on Unix servers. On Linux systems,
531 @ access to the /proc virtual filesystem is required, which means this limit
532 @ might not work inside a chroot() jail.
533 @ (Property: "max-loadavg")</p>
534 @
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
535 @ <hr>
536 @ <p><input type="submit" name="submit" value="Apply Changes"></p>
537 @ </div></form>
538 db_end_transaction(0);
539 style_finish_page();
540
+1 -46
--- src/style.c
+++ src/style.c
@@ -1390,55 +1390,10 @@
13901390
*/
13911391
void page_test_env(void){
13921392
webpage_error("");
13931393
}
13941394
1395
-/*
1396
-** WEBPAGE: honeypot
1397
-** This page is a honeypot for spiders and bots.
1398
-*/
1399
-void honeypot_page(void){
1400
- unsigned int uSeed = captcha_seed();
1401
- const char *zDecoded = captcha_decode(uSeed, 0);
1402
- int bAutoCaptcha = db_get_boolean("auto-captcha", 0);
1403
- char *zCaptcha = captcha_render(zDecoded);
1404
- style_header("I think you are a robot");
1405
- @ <p>You seem like a robot.</p>
1406
- @
1407
- @ <p>Is that incorrect? Are you really human?
1408
- @ If so, please prove it by transcribing the captcha text
1409
- @ into the entry box below and pressing "Submit".
1410
- @ <form action="%R/login" method="post">
1411
- @ <input type="hidden" id="u" name="u" value="anonymous">
1412
- @ <p>
1413
- @ Captcha: <input type="text" id="p" name="p" value="">
1414
- @ <input type="submit" name="in" value="Submit">
1415
- @
1416
- @ <p>Alternatively, you can <a href="%R/login">log in</a> using an
1417
- @ existing userid.
1418
- @
1419
- @ <p><input type="hidden" name="cs" value="%u(uSeed)">
1420
- @ <div class="captcha"><table class="captcha"><tr><td>\
1421
- @ <pre class="captcha">
1422
- @ %h(zCaptcha)
1423
- @ </pre></td></tr></table>
1424
- if( bAutoCaptcha ) {
1425
- @ <input type="button" value="Fill out captcha" id='autofillButton' \
1426
- @ data-af='%s(zDecoded)'>
1427
- builtin_request_js("login.js");
1428
- }
1429
- @ </div>
1430
- free(zCaptcha);
1431
- @
1432
- @ <p>We regret this inconvenience. However, robots have become so
1433
- @ prolific and so aggressive that they will soak up too much CPU time
1434
- @ and network bandwidth on our servers if allowed to run unchecked.
1435
- @ Your cooperation in demonstrating that you are human is
1436
- @ appreciated.
1437
- style_finish_page();
1438
-}
1439
-
14401395
/*
14411396
** Webpages that encounter an error due to missing or incorrect
14421397
** query parameters can jump to this routine to render an error
14431398
** message screen.
14441399
**
@@ -1487,11 +1442,11 @@
14871442
@ g.zHttpsURL = %h(g.zHttpsURL)<br>
14881443
@ g.zTop = %h(g.zTop)<br>
14891444
@ g.zPath = %h(g.zPath)<br>
14901445
@ g.userUid = %d(g.userUid)<br>
14911446
@ g.zLogin = %h(g.zLogin)<br>
1492
- @ g.isHuman = %d(g.isHuman)<br>
1447
+ @ g.isRobot = %d(g.isRobot)<br>
14931448
@ g.jsHref = %d(g.jsHref)<br>
14941449
if( g.zLocalRoot ){
14951450
@ g.zLocalRoot = %h(g.zLocalRoot)<br>
14961451
}else{
14971452
@ g.zLocalRoot = <i>none</i><br>
14981453
--- src/style.c
+++ src/style.c
@@ -1390,55 +1390,10 @@
1390 */
1391 void page_test_env(void){
1392 webpage_error("");
1393 }
1394
1395 /*
1396 ** WEBPAGE: honeypot
1397 ** This page is a honeypot for spiders and bots.
1398 */
1399 void honeypot_page(void){
1400 unsigned int uSeed = captcha_seed();
1401 const char *zDecoded = captcha_decode(uSeed, 0);
1402 int bAutoCaptcha = db_get_boolean("auto-captcha", 0);
1403 char *zCaptcha = captcha_render(zDecoded);
1404 style_header("I think you are a robot");
1405 @ <p>You seem like a robot.</p>
1406 @
1407 @ <p>Is that incorrect? Are you really human?
1408 @ If so, please prove it by transcribing the captcha text
1409 @ into the entry box below and pressing "Submit".
1410 @ <form action="%R/login" method="post">
1411 @ <input type="hidden" id="u" name="u" value="anonymous">
1412 @ <p>
1413 @ Captcha: <input type="text" id="p" name="p" value="">
1414 @ <input type="submit" name="in" value="Submit">
1415 @
1416 @ <p>Alternatively, you can <a href="%R/login">log in</a> using an
1417 @ existing userid.
1418 @
1419 @ <p><input type="hidden" name="cs" value="%u(uSeed)">
1420 @ <div class="captcha"><table class="captcha"><tr><td>\
1421 @ <pre class="captcha">
1422 @ %h(zCaptcha)
1423 @ </pre></td></tr></table>
1424 if( bAutoCaptcha ) {
1425 @ <input type="button" value="Fill out captcha" id='autofillButton' \
1426 @ data-af='%s(zDecoded)'>
1427 builtin_request_js("login.js");
1428 }
1429 @ </div>
1430 free(zCaptcha);
1431 @
1432 @ <p>We regret this inconvenience. However, robots have become so
1433 @ prolific and so aggressive that they will soak up too much CPU time
1434 @ and network bandwidth on our servers if allowed to run unchecked.
1435 @ Your cooperation in demonstrating that you are human is
1436 @ appreciated.
1437 style_finish_page();
1438 }
1439
1440 /*
1441 ** Webpages that encounter an error due to missing or incorrect
1442 ** query parameters can jump to this routine to render an error
1443 ** message screen.
1444 **
@@ -1487,11 +1442,11 @@
1487 @ g.zHttpsURL = %h(g.zHttpsURL)<br>
1488 @ g.zTop = %h(g.zTop)<br>
1489 @ g.zPath = %h(g.zPath)<br>
1490 @ g.userUid = %d(g.userUid)<br>
1491 @ g.zLogin = %h(g.zLogin)<br>
1492 @ g.isHuman = %d(g.isHuman)<br>
1493 @ g.jsHref = %d(g.jsHref)<br>
1494 if( g.zLocalRoot ){
1495 @ g.zLocalRoot = %h(g.zLocalRoot)<br>
1496 }else{
1497 @ g.zLocalRoot = <i>none</i><br>
1498
--- src/style.c
+++ src/style.c
@@ -1390,55 +1390,10 @@
1390 */
1391 void page_test_env(void){
1392 webpage_error("");
1393 }
1394
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1395 /*
1396 ** Webpages that encounter an error due to missing or incorrect
1397 ** query parameters can jump to this routine to render an error
1398 ** message screen.
1399 **
@@ -1487,11 +1442,11 @@
1442 @ g.zHttpsURL = %h(g.zHttpsURL)<br>
1443 @ g.zTop = %h(g.zTop)<br>
1444 @ g.zPath = %h(g.zPath)<br>
1445 @ g.userUid = %d(g.userUid)<br>
1446 @ g.zLogin = %h(g.zLogin)<br>
1447 @ g.isRobot = %d(g.isRobot)<br>
1448 @ g.jsHref = %d(g.jsHref)<br>
1449 if( g.zLocalRoot ){
1450 @ g.zLocalRoot = %h(g.zLocalRoot)<br>
1451 }else{
1452 @ g.zLocalRoot = <i>none</i><br>
1453
+1 -1
--- src/tar.c
+++ src/tar.c
@@ -760,11 +760,11 @@
760760
Blob tarball; /* Tarball accumulated here */
761761
const char *z;
762762
763763
login_check_credentials();
764764
if( !g.perm.Zip ){ login_needed(g.anon.Zip); return; }
765
- if( robot_squelch(900) ) return;
765
+ if( robot_restrict("zip") ) return;
766766
fossil_nice_default();
767767
zName = fossil_strdup(PD("name",""));
768768
z = P("r");
769769
if( z==0 ) z = P("uuid");
770770
if( z==0 ) z = tar_uuid_from_name(&zName);
771771
--- src/tar.c
+++ src/tar.c
@@ -760,11 +760,11 @@
760 Blob tarball; /* Tarball accumulated here */
761 const char *z;
762
763 login_check_credentials();
764 if( !g.perm.Zip ){ login_needed(g.anon.Zip); return; }
765 if( robot_squelch(900) ) return;
766 fossil_nice_default();
767 zName = fossil_strdup(PD("name",""));
768 z = P("r");
769 if( z==0 ) z = P("uuid");
770 if( z==0 ) z = tar_uuid_from_name(&zName);
771
--- src/tar.c
+++ src/tar.c
@@ -760,11 +760,11 @@
760 Blob tarball; /* Tarball accumulated here */
761 const char *z;
762
763 login_check_credentials();
764 if( !g.perm.Zip ){ login_needed(g.anon.Zip); return; }
765 if( robot_restrict("zip") ) return;
766 fossil_nice_default();
767 zName = fossil_strdup(PD("name",""));
768 z = P("r");
769 if( z==0 ) z = P("uuid");
770 if( z==0 ) z = tar_uuid_from_name(&zName);
771
--- src/timeline.c
+++ src/timeline.c
@@ -1830,10 +1830,11 @@
18301830
|| (bisectLocal && !g.perm.Setup)
18311831
){
18321832
login_needed(g.anon.Read && g.anon.RdTkt && g.anon.RdWiki);
18331833
return;
18341834
}
1835
+ if( zBefore && robot_restrict("timelineX") ) return;
18351836
if( !bisectLocal ){
18361837
etag_check(ETAG_QUERY|ETAG_COOKIE|ETAG_DATA|ETAG_CONFIG, 0);
18371838
}
18381839
cookie_read_parameter("y","y");
18391840
zType = P("y");
18401841
--- src/timeline.c
+++ src/timeline.c
@@ -1830,10 +1830,11 @@
1830 || (bisectLocal && !g.perm.Setup)
1831 ){
1832 login_needed(g.anon.Read && g.anon.RdTkt && g.anon.RdWiki);
1833 return;
1834 }
 
1835 if( !bisectLocal ){
1836 etag_check(ETAG_QUERY|ETAG_COOKIE|ETAG_DATA|ETAG_CONFIG, 0);
1837 }
1838 cookie_read_parameter("y","y");
1839 zType = P("y");
1840
--- src/timeline.c
+++ src/timeline.c
@@ -1830,10 +1830,11 @@
1830 || (bisectLocal && !g.perm.Setup)
1831 ){
1832 login_needed(g.anon.Read && g.anon.RdTkt && g.anon.RdWiki);
1833 return;
1834 }
1835 if( zBefore && robot_restrict("timelineX") ) return;
1836 if( !bisectLocal ){
1837 etag_check(ETAG_QUERY|ETAG_COOKIE|ETAG_DATA|ETAG_CONFIG, 0);
1838 }
1839 cookie_read_parameter("y","y");
1840 zType = P("y");
1841
+1 -1
--- src/zip.c
+++ src/zip.c
@@ -1012,11 +1012,11 @@
10121012
int eType = ARCHIVE_ZIP; /* Type of archive to generate */
10131013
char *zType; /* Human-readable archive type */
10141014
10151015
login_check_credentials();
10161016
if( !g.perm.Zip ){ login_needed(g.anon.Zip); return; }
1017
- if( robot_squelch(900) ) return;
1017
+ if( robot_restrict("zip") ) return;
10181018
if( fossil_strcmp(g.zPath, "sqlar")==0 ){
10191019
eType = ARCHIVE_SQLAR;
10201020
zType = "SQL";
10211021
/* For some reason, SQL-archives are like catnip for robots. So
10221022
** don't allow them to be downloaded by user "nobody" */
10231023
--- src/zip.c
+++ src/zip.c
@@ -1012,11 +1012,11 @@
1012 int eType = ARCHIVE_ZIP; /* Type of archive to generate */
1013 char *zType; /* Human-readable archive type */
1014
1015 login_check_credentials();
1016 if( !g.perm.Zip ){ login_needed(g.anon.Zip); return; }
1017 if( robot_squelch(900) ) return;
1018 if( fossil_strcmp(g.zPath, "sqlar")==0 ){
1019 eType = ARCHIVE_SQLAR;
1020 zType = "SQL";
1021 /* For some reason, SQL-archives are like catnip for robots. So
1022 ** don't allow them to be downloaded by user "nobody" */
1023
--- src/zip.c
+++ src/zip.c
@@ -1012,11 +1012,11 @@
1012 int eType = ARCHIVE_ZIP; /* Type of archive to generate */
1013 char *zType; /* Human-readable archive type */
1014
1015 login_check_credentials();
1016 if( !g.perm.Zip ){ login_needed(g.anon.Zip); return; }
1017 if( robot_restrict("zip") ) return;
1018 if( fossil_strcmp(g.zPath, "sqlar")==0 ){
1019 eType = ARCHIVE_SQLAR;
1020 zType = "SQL";
1021 /* For some reason, SQL-archives are like catnip for robots. So
1022 ** don't allow them to be downloaded by user "nobody" */
1023

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button