Check-in [488afb9746]
Not logged in
Overview

SHA1 Hash:488afb97461a34ae63ac2a1f5e8b018e68466686
Date: 2007-10-06 17:10:47
User: drh
Comment:Enforce well-formedness constraints on wiki pagenames.
Timelines: ancestors | descendants | both | trunk
Other Links: files | ZIP archive | manifest

Tags And Properties
Changes
[hide diffs]

Modified src/manifest.c from [fb34bded33] to [d51cf77a0b].

@@ -370,10 +370,13 @@
         if( p->zWikiTitle!=0 ) goto manifest_syntax_error;
         if( blob_token(&line, &a1)==0 ) goto manifest_syntax_error;
         if( blob_token(&line, &a2)!=0 ) goto manifest_syntax_error;
         p->zWikiTitle = blob_terminate(&a1);
         defossilize(p->zWikiTitle);
+        if( !wiki_name_is_wellformed(p->zWikiTitle) ){
+          goto manifest_syntax_error;
+        }
         break;
       }
 
       /*
       **    M <uuid>

Modified src/wiki.c from [1ab1276742] to [22c7d6d4bb].

@@ -25,10 +25,53 @@
 */
 #include <assert.h>
 #include "config.h"
 #include "wiki.h"
 
+/*
+** Return true if the input string is a well-formed wiki page name.
+**
+** Well-formed wiki page names do not begin or end with whitespace,
+** and do not contain tabs or other control characters and do not
+** contain more than a single space character in a row.  Well-formed
+** names must be between 3 and 100 chracters in length, inclusive.
+*/
+int wiki_name_is_wellformed(const char *z){
+  int i;
+  if( z[0]<=0x20 ){
+    return 0;
+  }
+  for(i=1; z[i]; i++){
+    if( z[i]<0x20 ) return 0;
+    if( z[i]==0x20 && z[i-1]==0x20 ) return 0;
+  }
+  if( z[i-1]==' ' ) return 0;
+  if( i<3 || i>100 ) return 0;
+  return 1;
+}
+
+/*
+** Check a wiki name.  If it is not well-formed, then issue an error
+** and return true.  If it is well-formed, return false.
+*/
+static int check_name(const char *z){
+  if( !wiki_name_is_wellformed(z) ){
+    style_header("Wiki Page Name Error");
+    @ The wiki name "<b>%h(z)</b>" is not well-formed.  Rules for
+    @ wiki page names:
+    @ <ul>
+    @ <li> Must not begin or end with a space.
+    @ <li> Must not contain any control characters, including tab or
+    @      newline.
+    @ <li> Must not have two or more spaces in a row internally.
+    @ <li> Must be between 3 and 100 characters in length.
+    @ </ul>
+    style_footer();
+    return 1;
+  }
+  return 0;
+}
 
 /*
 ** WEBPAGE: wiki
 ** URL: /wiki/PAGENAME
 */
@@ -43,10 +86,11 @@
 
   login_check_credentials();
   if( !g.okRdWiki ){ login_needed(); return; }
   zPageName = mprintf("%s", g.zExtra);
   dehttpize(zPageName);
+  if( check_name(zPageName) ) return;
   zTag = mprintf("wiki-%s", zPageName);
   rid = db_int(0,
     "SELECT rid FROM tagxref"
     " WHERE tagid=(SELECT tagid FROM tag WHERE tagname=%Q)"
     " ORDER BY mtime DESC", zTag
@@ -66,11 +110,11 @@
   style_header(zHtmlPageName);
   blob_init(&wiki, zBody, -1);
   wiki_convert(&wiki, 0);
   blob_reset(&wiki);
   manifest_clear(&m);
-  if( zPageName[0] && ((rid && g.okWrWiki) || (!rid && g.okNewWiki)) ){
+  if( (rid && g.okWrWiki) || (!rid && g.okNewWiki) ){
     @ <hr>
     @ [<a href="%s(g.zBaseURL)/wikiedit/%s(g.zExtra)">Edit</a>]
   }
   style_footer();
 }
@@ -94,10 +138,11 @@
     zBody = mprintf("%s", zBody);
   }
   login_check_credentials();
   zPageName = mprintf("%s", g.zExtra);
   dehttpize(zPageName);
+  if( check_name(zPageName) ) return;
   zTag = mprintf("wiki-%s", zPageName);
   rid = db_int(0,
     "SELECT rid FROM tagxref"
     " WHERE tagid=(SELECT tagid FROM tag WHERE tagname=%Q)"
     " ORDER BY mtime DESC", zTag
@@ -146,11 +191,11 @@
     blob_reset(&wiki);
     content_deltify(rid, nrid, 0);
     db_end_transaction(0);
     cgi_redirect(mprintf("wiki/%s", g.zExtra));
   }
-  if( P("cancel")!=0 || zPageName[0]==0 ){
+  if( P("cancel")!=0 ){
     cgi_redirect(mprintf("wiki/%s", g.zExtra));
     return;
   }
   if( zBody==0 ){
     zBody = mprintf("<i>Empty Page</i>");

Modified src/wikiformat.c from [6d7f89510a] to [83901f90e7].

@@ -760,12 +760,14 @@
    || strncmp(zTarget, "https:", 6)==0
    || strncmp(zTarget, "ftp:", 4)==0
    || strncmp(zTarget, "mailto:", 7)==0
   ){
     blob_appendf(p->pOut, zTarget);
-  }else{
+  }else if( wiki_name_is_wellformed(zTarget) ){
     blob_appendf(p->pOut, "%s/wiki/%T", g.zBaseURL, zTarget);
+  }else{
+    blob_appendf(p->pOut, "error");
   }
 }
 
 /*
 ** Check to see if the given parsed markup is the correct
@@ -862,18 +864,19 @@
         break;
       }
       case TOKEN_LINK: {
         char *zTarget;
         char *zDisplay = 0;
-        int i;
+        int i, j;
         int savedState;
         addMissingMarkup(p);
         zTarget = &z[1];
         for(i=1; z[i] && z[i]!=']'; i++){
           if( z[i]=='|' && zDisplay==0 ){
             zDisplay = &z[i+1];
             z[i] = 0;
+            for(j=i-1; j>0 && isspace(z[j]); j--){ z[j] = 0; }
           }
         }
         z[i] = 0;
         if( zDisplay==0 ){
           zDisplay = zTarget;