Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
| Comment: | Have the security-audit page analyze and display the content security policy. |
|---|---|
| Downloads: | Tarball | ZIP archive |
| Timelines: | family | ancestors | descendants | both | trunk |
| Files: | files | file ages | folders |
| SHA3-256: |
9cf90a4f9d134b7fddb190e27c33c8d5 |
| User & Date: | drh 2019-08-19 17:18:31.499 |
Context
|
2019-08-20
| ||
| 02:09 | Fix a compiler warning in the security-audit page. ... (check-in: 3243a6c148 user: drh tags: trunk) | |
| 01:34 | Added --with-sanitizer configure-time option for appending -fsanitize=VALUE to CFLAGS and LDFLAGS, plus automatic detection of -lubsan for GCC, which doesn't automatically link to that with -fsanitize=undefined as Clang does. EDIT: This check-in breaks the built on Ubuntu 18.04. ... (check-in: 7907b6ffae user: wyoung tags: configure-updates) | |
|
2019-08-19
| ||
| 17:18 | Have the security-audit page analyze and display the content security policy. ... (check-in: 9cf90a4f9d user: drh tags: trunk) | |
| 13:04 | Increase the default HTTP request timeout to 10 minutes. Provide the FOSSIL_DEFAULT_TIMEOUT compile-time option for setting an alternative default. ... (check-in: 7979989dff user: drh tags: trunk) | |
Changes
Changes to src/cgi.c.
| ︙ | ︙ | |||
142 143 144 145 146 147 148 149 150 151 152 153 154 155 |
/*
** Return a pointer to the CGI output blob.
*/
Blob *cgi_output_blob(void){
return pContent;
}
/*
** Combine the header and body of the CGI into a single string.
*/
static void cgi_combine_header_and_body(void){
int size = blob_size(&cgiContent[1]);
if( size>0 ){
| > > > > > > > | 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 |
/*
** Return a pointer to the CGI output blob.
*/
Blob *cgi_output_blob(void){
return pContent;
}
/*
** Return complete text of the output header
*/
const char *cgi_header(void){
return blob_str(&cgiContent[0]);
}
/*
** Combine the header and body of the CGI into a single string.
*/
static void cgi_combine_header_and_body(void){
int size = blob_size(&cgiContent[1]);
if( size>0 ){
|
| ︙ | ︙ |
Changes to src/security_audit.c.
| ︙ | ︙ | |||
30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
while( zTest[0] ){
if( strchr(zCap, zTest[0]) ) return 1;
zTest++;
}
return 0;
}
/*
** WEBPAGE: secaudit0
**
** Run a security audit of the current Fossil setup, looking
** for configuration problems that might allow unauthorized
** access to the repository.
**
** This page requires administrator access. It is usually
** accessed using the Admin/Security-Audit menu option
** from any of the default skins.
*/
void secaudit0_page(void){
const char *zAnonCap; /* Capabilities of user "anonymous" and "nobody" */
const char *zPubPages; /* GLOB pattern for public pages */
const char *zSelfCap; /* Capabilities of self-registered users */
char *z;
int n;
login_check_credentials();
if( !g.perm.Admin ){
login_needed(0);
return;
}
style_header("Security Audit");
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 |
while( zTest[0] ){
if( strchr(zCap, zTest[0]) ) return 1;
zTest++;
}
return 0;
}
/*
** Extract the content-security-policy from the reply header. Parse it
** up into separate fields, and return a pointer to a null-terminated
** array of pointers to strings, one entry for each field. Or return
** a NULL pointer if no CSP could be located in the header.
**
** Memory to hold the returned array and of the strings is obtained from
** a single memory allocation, which the caller should free to avoid a
** memory leak.
*/
static char **parse_content_security_policy(void){
char **azCSP = 0;
int nCSP = 0;
const char *zHeader;
const char *zAll;
char *zCopy;
int nAll = 0;
int ii, jj, n, nx = 0;
int nSemi;
zHeader = cgi_header();
if( zHeader==0 ) return 0;
for(ii=0; zHeader[ii]; ii+=n){
n = html_token_length(zHeader+ii);
if( zHeader[ii]=='<'
&& fossil_strnicmp(html_attribute(zHeader+ii,"http-equiv",&nx),
"Content-Security-Policy",23)==0
&& nx==23
&& (zAll = html_attribute(zHeader+ii,"content",&nAll))!=0
){
for(jj=nSemi=0; jj<nAll; jj++){ if( zAll[jj]==';' ) nSemi++; }
azCSP = fossil_malloc( nAll+1 + (nSemi+2)*sizeof(char*) );
zCopy = &azCSP[nSemi+2];
memcpy(zCopy,zAll,nAll);
zCopy[nAll] = 0;
while( fossil_isspace(zCopy[0]) || zCopy[0]==';' ){ zCopy++; }
azCSP[0] = zCopy;
nCSP = 1;
for(jj=0; zCopy[jj]; jj++){
if( zCopy[jj]==';' ){
int k;
for(k=jj-1; k>0 && fossil_isspace(zCopy[k]); k--){ zCopy[k] = 0; }
zCopy[jj] = 0;
while( jj+1<nAll
&& (fossil_isspace(zCopy[jj+1]) || zCopy[jj+1]==';')
){
jj++;
}
assert( nCSP<nSemi+1 );
azCSP[nCSP++] = zCopy+jj;
}
}
assert( nCSP<=nSemi+2 );
azCSP[nCSP] = 0;
return azCSP;
}
}
return 0;
}
/*
** WEBPAGE: secaudit0
**
** Run a security audit of the current Fossil setup, looking
** for configuration problems that might allow unauthorized
** access to the repository.
**
** This page requires administrator access. It is usually
** accessed using the Admin/Security-Audit menu option
** from any of the default skins.
*/
void secaudit0_page(void){
const char *zAnonCap; /* Capabilities of user "anonymous" and "nobody" */
const char *zPubPages; /* GLOB pattern for public pages */
const char *zSelfCap; /* Capabilities of self-registered users */
char *z;
int n;
char **azCSP; /* Parsed content security policy */
login_check_credentials();
if( !g.perm.Admin ){
login_needed(0);
return;
}
style_header("Security Audit");
|
| ︙ | ︙ | |||
437 438 439 440 441 442 443 444 445 446 447 448 449 450 |
@ at <a href='%R/extfilelist'>%h(g.zExtRoot)</a> holding
@ %d(nCgi) CGIs and %d(nFile-nCgi) static content and data files.
}
@ <li><p> User capability summary:
capability_summary();
if( alert_enabled() ){
@ <li><p> Email alert configuration summary:
@ <table class="label-value">
stats_for_email();
@ </table>
}else{
@ <li><p> Email alerts are disabled
| > > > > > > > > > > > > > > > > > > > > | 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 |
@ at <a href='%R/extfilelist'>%h(g.zExtRoot)</a> holding
@ %d(nCgi) CGIs and %d(nFile-nCgi) static content and data files.
}
@ <li><p> User capability summary:
capability_summary();
azCSP = parse_content_security_policy();
if( azCSP==0 ){
@ <li><p> WARNING: No Content Security Policy (CSP) is specified in the
@ header. Though not required, a strong CSP is recommended. Fossil will
@ automatically insert an appropriate CSP if you let it generate the
@ HTML <tt><head></tt> element by omitting <tt><body></tt>
@ from the header configuration in your customized skin.
@
}else{
int ii;
@ <li><p> Content Security Policy:
@ <ol type="a">
for(ii=0; azCSP[ii]; ii++){
@ <li>%h(azCSP[ii])
}
@ </ol>
}
fossil_free(azCSP);
if( alert_enabled() ){
@ <li><p> Email alert configuration summary:
@ <table class="label-value">
stats_for_email();
@ </table>
}else{
@ <li><p> Email alerts are disabled
|
| ︙ | ︙ |
Changes to src/wikiformat.c.
| ︙ | ︙ | |||
2012 2013 2014 2015 2016 2017 2018 |
}
z += n;
}
free(renderer.aStack);
}
/*
| | < < < | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 2026 2027 2028 2029 2030 2031 2032 2033 2034 2035 2036 2037 2038 2039 2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 2050 2051 2052 2053 2054 2055 2056 2057 2058 2059 2060 2061 2062 2063 2064 2065 2066 2067 2068 2069 2070 2071 2072 2073 2074 2075 2076 2077 2078 2079 2080 2081 2082 2083 2084 2085 2086 2087 2088 2089 2090 2091 2092 2093 2094 2095 2096 2097 2098 2099 2100 2101 2102 2103 2104 2105 2106 2107 2108 2109 2110 2111 2112 2113 2114 2115 2116 2117 2118 2119 2120 2121 2122 2123 2124 2125 2126 2127 2128 2129 2130 2131 2132 2133 2134 2135 2136 2137 2138 2139 2140 2141 2142 2143 2144 2145 2146 2147 2148 2149 2150 |
}
z += n;
}
free(renderer.aStack);
}
/*
** Return the length, in bytes, of the HTML token that z is pointing to.
*/
int html_token_length(const char *z){
int n;
char c;
if( (c=z[0])=='<' ){
n = htmlTagLength(z);
if( n<=0 ) n = 1;
}else if( fossil_isspace(c) ){
for(n=1; z[n] && fossil_isspace(z[n]); n++){}
}else if( c=='&' ){
n = z[1]=='#' ? 2 : 1;
while( fossil_isalnum(z[n]) ) n++;
if( z[n]==';' ) n++;
}else{
n = 1;
for(n=1; 1; n++){
if( (c = z[n]) > '<' ) continue;
if( c=='<' || c=='&' || fossil_isspace(c) || c==0 ) break;
}
}
return n;
}
/*
** z points to someplace in the middle of HTML markup. Return the length
** of the subtoken that starts on z.
*/
int html_subtoken_length(const char *z){
int n;
char c;
c = z[0];
if( fossil_isspace(c) ){
for(n=1; z[n] && fossil_isspace(z[n]); n++){}
return n;
}
if( c=='"' || c=='\'' ){
for(n=1; z[n] && z[n]!=c && z[n]!='>'; n++){}
if( z[n]==c ) n++;
return n;
}
if( c=='>' ){
return 0;
}
if( c=='=' ){
return 1;
}
if( fossil_isalnum(c) || c=='/' ){
for(n=1; (c=z[n])!=0 && (fossil_isalnum(c) || c=='-' || c=='_'); n++){}
return n;
}
return 1;
}
/*
** z points to an HTML markup token: <TAG ATTR=VALUE ...>
** This routine looks for the VALUE associated with zAttr and returns
** a pointer to the start of that value and sets *pLen to be the length
** in bytes for the value. Or it returns NULL if no such attr exists.
*/
const char *html_attribute(const char *zMarkup, const char *zAttr, int *pLen){
int i = 1;
int n;
int nAttr;
int iMatchCnt = 0;
assert( zMarkup[0]=='<' );
assert( zMarkup[1]!=0 );
n = html_subtoken_length(zMarkup+i);
if( n==0 ) return 0;
i += n;
nAttr = (int)strlen(zAttr);
while( 1 ){
const char *zStart = zMarkup+i;
n = html_subtoken_length(zStart);
if( n==0 ) break;
i += n;
if( fossil_isspace(zStart[0]) ) continue;
if( n==nAttr && fossil_strnicmp(zAttr,zStart,nAttr)==0 ){
iMatchCnt = 1;
}else if( n==1 && zStart[0]=='=' && iMatchCnt==1 ){
iMatchCnt = 2;
}else if( iMatchCnt==2 ){
if( (zStart[0]=='"' || zStart[0]=='\'') && zStart[n-1]==zStart[0] ){
zStart++;
n -= 2;
}
*pLen = n;
return zStart;
}else{
iMatchCnt = 0;
}
}
return 0;
}
/*
** COMMAND: test-html-tokenize
**
** Tokenize an HTML file. Return the offset and length and text of
** each token - one token per line. Omit white-space tokens.
*/
void test_html_tokenize(void){
Blob in;
char *z;
int i;
int iOfst, n;
for(i=2; i<g.argc; i++){
blob_read_from_file(&in, g.argv[i], ExtFILE);
z = blob_str(&in);
for(iOfst=0; z[iOfst]; iOfst+=n){
n = html_token_length(z+iOfst);
if( fossil_isspace(z[iOfst]) ) continue;
fossil_print("%d %d %.*s\n", iOfst, n, n, z+iOfst);
if( z[iOfst]=='<' && n>1 ){
int j,k;
for(j=iOfst+1; (k = html_subtoken_length(z+j))>0; j+=k){
if( fossil_isspace(z[j]) || z[j]=='=' ) continue;
fossil_print("# %d %d %.*s\n", j, k, k, z+j);
}
}
}
blob_reset(&in);
}
}
/*
** Attempt to reformat messy HTML to be easily readable by humans.
**
** * Try to keep lines less than 80 characters in length
** * Collapse white space into a single space
** * Put a blank line before:
|
| ︙ | ︙ | |||
2060 2061 2062 2063 2064 2065 2066 |
void htmlTidy(const char *zIn, Blob *pOut){
int n;
int nPre = 0;
int iCur = 0;
int wantSpace = 0;
int omitSpace = 1;
while( zIn[0] ){
| | | 2159 2160 2161 2162 2163 2164 2165 2166 2167 2168 2169 2170 2171 2172 2173 |
void htmlTidy(const char *zIn, Blob *pOut){
int n;
int nPre = 0;
int iCur = 0;
int wantSpace = 0;
int omitSpace = 1;
while( zIn[0] ){
n = html_token_length(zIn);
if( zIn[0]=='<' && n>1 ){
int i, j;
int isCloseTag;
int eTag;
int eType;
char zTag[32];
isCloseTag = zIn[1]=='/';
|
| ︙ | ︙ | |||
2179 2180 2181 2182 2183 2184 2185 |
int i, j;
int inTitle = 0; /* True between <title>...</title> */
int seenText = 0; /* True after first non-whitespace seen */
int nNL = 0; /* Number of \n characters at the end of pOut */
int nWS = 0; /* True if pOut ends with whitespace */
while( fossil_isspace(zIn[0]) ) zIn++;
while( zIn[0] ){
| | | | 2278 2279 2280 2281 2282 2283 2284 2285 2286 2287 2288 2289 2290 2291 2292 2293 2294 2295 2296 2297 2298 2299 2300 2301 2302 2303 2304 2305 2306 2307 2308 |
int i, j;
int inTitle = 0; /* True between <title>...</title> */
int seenText = 0; /* True after first non-whitespace seen */
int nNL = 0; /* Number of \n characters at the end of pOut */
int nWS = 0; /* True if pOut ends with whitespace */
while( fossil_isspace(zIn[0]) ) zIn++;
while( zIn[0] ){
n = html_token_length(zIn);
if( zIn[0]=='<' && n>1 ){
int isCloseTag;
int eTag;
int eType;
char zTag[32];
isCloseTag = zIn[1]=='/';
for(i=0, j=1+isCloseTag; i<30 && fossil_isalnum(zIn[j]); i++, j++){
zTag[i] = fossil_tolower(zIn[j]);
}
zTag[i] = 0;
eTag = findTag(zTag);
eType = aMarkup[eTag].iType;
if( eTag==MARKUP_INVALID && fossil_strnicmp(zIn,"<style",6)==0 ){
zIn += n;
while( zIn[0] ){
n = html_token_length(zIn);
if( fossil_strnicmp(zIn, "</style",7)==0 ) break;
zIn += n;
}
if( zIn[0]=='<' ) zIn += n;
continue;
}
if( eTag==MARKUP_TITLE ){
|
| ︙ | ︙ |