Diff for /parser3/src/main/untaint.C between versions 1.73 and 1.82

version 1.73, 2001/10/29 16:59:30 version 1.82, 2001/11/22 10:29:47
Line 2 Line 2
         Parser: String class part: untaint mechanizm.          Parser: String class part: untaint mechanizm.
   
         Copyright(c) 2001 ArtLebedev Group(http://www.artlebedev.com)          Copyright(c) 2001 ArtLebedev Group(http://www.artlebedev.com)
         Author: Alexander Petrosyan <paf@design.ru>(http://design.ru/paf)          Author: Alexander Petrosyan <paf@design.ru>(http://paf.design.ru)
   
         $Id$          $Id$
 */  */
Line 84  static const char * String_Untaint_lang_ Line 84  static const char * String_Untaint_lang_
         "SQL",       ///< ^table:sql body          "SQL",       ///< ^table:sql body
         "JS",        ///< JavaScript code          "JS",        ///< JavaScript code
         "XML",          ///< ^dom:set xml          "XML",          ///< ^dom:set xml
         "HTML",      ///< HTML code (for editing)          "HTML"      ///< HTML code (for editing)
         "UHTML", ///< HTML code with USER chars  
 };  };
   
   
 // String  // String
   
 static bool typo_present(Array::Item *value, const void *info) {  
         Array *row=static_cast<Array *>(value);  
         const char *src=static_cast<const char *>(info);  
   
         int partial;  
         row->get_string(0)->cmp(partial, src);  
         return   
                 partial==0 || // full match  
                 partial==1; // typo left column starts 'src'  
 }  
   
 /*  /*
   
 HTTP-header    = field-name ":" [ field-value ] CRLF  HTTP-header    = field-name ":" [ field-value ] CRLF
Line 144  inline bool need_quote_http_header(const Line 132  inline bool need_quote_http_header(const
         return false;          return false;
 }  }
   
 /** @todo maybe additional check "are all pieces are clean?" would be profitable?  String& String::append(const String& src, uchar lang, bool forced) {
         @todo fix potential forigins_mode buf overrun          // manually unrolled code to avoid do{if(const)} constructs
 */          if(forced) 
 size_t String::cstr_bufsize(Untaint_lang lang) const {                  STRING_SRC_FOREACH_ROW(
         return  (                          APPEND(row->item.ptr, row->item.size, 
                         lang==UL_AS_IS?                                  lang, //forcing passed lang
                                 size()                                  row->item.origin.file, row->item.origin.line);
                         :                  )
                                 size()          else if(lang==UL_PASS_APPENDED) 
                                 *UNTAINT_TIMES_BIGGER                  STRING_SRC_FOREACH_ROW(
                                 *(forigins_mode?10:1)                          APPEND(row->item.ptr, row->item.size, 
                 )                                   row->item.lang, // passing item's lang
                 +1;                                  row->item.origin.file, row->item.origin.line);
                   )
           else if(lang&UL_OPTIMIZE_BIT) // main idea here
                   // tainted piece would get OPTIMIZED bit from 'lang'
                   // clean piece would be marked OPTIMIZED manually
                   // pieces with determined languages [not tainted|clean] would retain theirs langs
                   STRING_SRC_FOREACH_ROW(
                           APPEND(row->item.ptr, row->item.size, 
                                   row->item.lang==UL_TAINTED?lang:(
                                           row->item.lang==UL_CLEAN?UL_CLEAN|UL_OPTIMIZE_BIT: // ORing with OPTIMIZED flag
                                                   row->item.lang
                                   ), 
                                   row->item.origin.file, row->item.origin.line);
                   )
           else
                   STRING_SRC_FOREACH_ROW(
                           APPEND(row->item.ptr, row->item.size, 
                                   row->item.lang==UL_TAINTED?lang:row->item.lang,
                                   row->item.origin.file, row->item.origin.line);
                   );
   break2:
           return *this;
   }
   
   size_t String::cstr_bufsize(Untaint_lang lang,
                                                           SQL_Connection *connection,
                                                           const char *charset) const {
           size_t dest=1; // for terminating 0
           STRING_FOREACH_ROW(
                   uchar to_lang=lang==UL_UNSPECIFIED?row->item.lang:lang;
   
                   switch(to_lang & ~UL_OPTIMIZE_BIT) {
                   case UL_CLEAN:
                   case UL_TAINTED:
                   case UL_AS_IS:
                           // clean piece
   
                           // tainted piece, but undefined untaint language
                           // for VString.as_double of tainted values
                           // for ^process{body} evaluation
   
                           // tainted, untaint language: as-is
                           dest+=row->item.size;
                           break;
                   case UL_FILE_SPEC:
                           // tainted, untaint language: file [name]
                           dest+=row->item.size*3/* worst: Z->%XX */;
                           break;
                   case UL_URI:
                           // tainted, untaint language: uri
                           dest+=row->item.size*3/* worst: Z->%XX */;
                           break;
                   case UL_HTTP_HEADER:
                           // tainted, untaint language: http-field-content-text
                           dest+=row->item.size*3/* worst: Z->%XX */;
                           break;
                   case UL_MAIL_HEADER:
                           // tainted, untaint language: mail-header
                           if(charset) {
                                   // Subject: Re: parser3: =?koi8-r?Q?=D3=C5=CD=C9=CE=C1=D2?=
                                   dest+=row->item.size*3+MAX_STRING/* worst: =?charset?Q?=%XX?= */;
                           } else {
                                   dest+=row->item.size;
                           }
                           break;
                   case UL_TABLE: 
                           // tainted, untaint language: table
                           dest+=row->item.size;
                           break;
                   case UL_SQL:
                           // tainted, untaint language: sql
                           if(connection)
                                   dest+=connection->quote(0, row->item.ptr, row->item.size);
                           break;
                   case UL_JS:
                           escape(switch(*src) {
                                   case '"': case '\'': case '\n': case '\\': case '\xFF':
                                           dest+=2;  break;
                                   default: 
                                           dest++;  break;
                           });
                           break;
                   case UL_XML:
                           escape(switch(*src) {
                                   case '&': case '>': case '<': case '"': case '\'': 
                                           dest+= 6;  break;
                                   default: 
                                           dest++;  break;
                           });
                           break;
                   case UL_HTML:
                           escape(switch(*src) {
                                   case '&': 
                                   case '>': 
                                   case '<': 
                                   case '"': 
                                           dest+=6;  break;
                                   default: 
                                           dest++;  break;
                           });
                           break;
                   }
           );
   break2:
           return dest;
 }  }
   
 /** @todo fix theoretical \n mem overrun in TYPO replacements  
 */  
 char *String::store_to(char *dest, Untaint_lang lang,   char *String::store_to(char *dest, Untaint_lang lang, 
                                            SQL_Connection *connection,                                             SQL_Connection *connection,
                                            const char *charset) const {                                             const char *charset) const {
         // $MAIN:html-typo table          // WARNING:
         Dictionary *user_typo_dict=static_cast<Dictionary *>(pool().tag());          //       before any changes check cstr_bufsize first!!!
         Dictionary *typo_dict=user_typo_dict?user_typo_dict:default_typo_dict;  
   
         bool whitespace=true;          bool whitespace=true;
           // expanded STRING_FOREACH_ROW here for debugging purposes
           const Chunk *chunk=&head;  \
           do { \
                   const Chunk::Row *row=chunk->rows; \
                   for(uint i=0; i<chunk->count; i++, row++) { \
                           if(row==append_here) \
                                   goto break2; \
                           \
                   uchar to_lang=lang==UL_UNSPECIFIED?row->item.lang:lang;
   
                   char *start=dest;
   
                   switch(to_lang & ~UL_OPTIMIZE_BIT) {
                   case UL_CLEAN:
                   case UL_TAINTED:
                   case UL_AS_IS:
                           // clean piece
   
                           // tainted piece, but undefined untaint language
                           // for VString.as_double of tainted values
                           // for ^process{body} evaluation
   
                           // tainted, untaint language: as-is
                           memcpy(dest, row->item.ptr, row->item.size); 
                           dest+=row->item.size;
                           break;
                   case UL_FILE_SPEC:
                           // tainted, untaint language: file [name]
                           escape(switch(*src) {
                                   case ' ': to_char('_');  break;
                                   encode(need_file_encode, '+');
                           });
                           break;
                   case UL_URI:
                           // tainted, untaint language: uri
                           escape(switch(*src) {
                                   case ' ': to_char('+');  break;
                                   encode(need_uri_encode, '%');
                           });
                           break;
                   case UL_HTTP_HEADER:
                           // tainted, untaint language: http-field-content-text
                           escape(switch(*src) {
                                   case ' ': to_char('+');  break;
                                   encode(need_uri_encode, '%');
                           });
                           break;
                   case UL_MAIL_HEADER:
                           // tainted, untaint language: mail-header
                           if(charset) {
                                   // Subject: Re: parser3: =?koi8-r?Q?=D3=C5=CD=C9=CE=C1=D2?=
                                   const char *src=row->item.ptr; 
                                   bool to_quoted_printable=false;
                                   for(int size=row->item.size; size--; src++) {
                                           if(*src & 0x80) {
                                                   if(!to_quoted_printable) {
                                                           dest+=sprintf(dest, "=?%.15s?Q?", charset);
                                                           to_quoted_printable=true;
                                                   }
                                                   dest+=sprintf(dest, "=%02X", *src & 0xFF);
                                           } else {
                                                   *dest++=*src;                                           
                                           }
                                   }
                                   if(to_quoted_printable) // close
                                           dest+=sprintf(dest, "?=");
                           } else {
                                   memcpy(dest, row->item.ptr, row->item.size); 
                                   dest+=row->item.size;
                           }
                           break;
                   case UL_TABLE: 
                           // tainted, untaint language: table
                           escape(switch(*src) {
                                   case '\t': to_char(' ');  break;
                                   case '\n': to_char(' ');  break;
                                   _default;
                           });
                           break;
                   case UL_SQL:
                           // tainted, untaint language: sql
                           if(connection)
                                   dest+=connection->quote(dest, row->item.ptr, row->item.size);
                           else
                                   throw Exception(0, 0,
                                           this,
                                           "untaint in SQL language failed - no connection specified");
                           break;
                   case UL_JS:
                           escape(switch(*src) {
                                   case '"': to_string("\\\"", 2);  break;
                                   case '\'': to_string("\\'", 2);  break;
                                   case '\n': to_string("\\n", 2);  break;
                                   case '\\': to_string("\\\\", 2);  break;
                                   case '\xFF': to_string("\\\xFF", 2);  break;
                                   _default;
                           });
                           break;
                   case UL_XML:
                           escape(switch(*src) {
                                   case '&': to_string("&amp;", 5);  break;
                                   case '>': to_string("&gt;", 4);  break;
                                   case '<': to_string("&lt;", 4);  break;
                                   case '"': to_string("&quot;", 6);  break;
                                   case '\'': to_string("&apos;", 6);  break;
                                   _default;
                           });
                           break;
                   case UL_HTML:
                           escape(switch(*src) {
                                   case '&': to_string("&amp;", 5);  break;
                                   case '>': to_string("&gt;", 4);  break;
                                   case '<': to_string("&lt;", 4);  break;
                                   case '"': to_string("&quot;", 6);  break;
                                   _default;
                           });
                           break;
                   default:
                           throw Exception(0, 0, 
                                   this, 
                                   "unknown untaint language #%d", 
                                           static_cast<int>(row->item.lang)); // sould never
                           break; // never
                   }
   
                   if(to_lang & UL_OPTIMIZE_BIT) { 
                           // optimizing whitespace
                           char *stop=dest;  dest=start;
                           for(char *src=start; src<stop; src++)
                                   switch(*src) {
                                   // of all consequent white space chars leaving only first one
                                   case ' ': case '\r': case '\n': case '\t':
                                           if(!whitespace) {
                                                   *dest++=*src;
                                                   whitespace=true;
                                           }
                                           break;
                                   default:
                                           whitespace=false;
                                           *dest++=*src;
                                           break;
                                   };
                   } else // piece without optimization
                           whitespace=false;
   
                   } \
                   chunk=row->link; \
           } while(chunk); \
   
   break2:
           return dest;
   }
   
   char *String::cstr_debug_origins() const {
           //_asm int 3;
           char *result=(char *)malloc(size()+used_rows()*MAX_STRING*2);
           char *dest=result;
           
         const Chunk *chunk=&head;           const Chunk *chunk=&head; 
         do {          do {
                 const Chunk::Row *row=chunk->rows;                  const Chunk::Row *row=chunk->rows;
Line 176  char *String::store_to(char *dest, Untai Line 422  char *String::store_to(char *dest, Untai
                         if(row==append_here)                          if(row==append_here)
                                 goto break2;                                  goto break2;
   
                         Untaint_lang to_lang=lang==UL_UNSPECIFIED?(Untaint_lang)row->item.lang:lang;  
   
                         char *dest_before_origins=dest;  
   
                         if(forigins_mode) {  
 #ifndef NO_STRING_ORIGIN  #ifndef NO_STRING_ORIGIN
                                 if(row->item.origin.file)                          if(row->item.origin.file)
                                         dest+=sprintf(dest, "%s(%d)",                                  dest+=sprintf(dest, ORIGIN_FILE_LINE_FORMAT,
                                                 row->item.origin.file,                                          row->item.origin.file,
                                                 1+row->item.origin.line);                                          1+row->item.origin.line);
                                 else                          else
                                         dest+=sprintf(dest, "unknown");                                  dest+=sprintf(dest, "<unknown>");
 #endif  #endif
                                 dest+=sprintf(dest, "#%s: ",                          uchar show_lang=row->item.lang & ~UL_OPTIMIZE_BIT;
                                         String_Untaint_lang_name[to_lang]);                          if(show_lang>=sizeof(String_Untaint_lang_name)/sizeof(String_Untaint_lang_name[0]))
                         }  
                         char *dest_after_origins=dest;  
   
                         // WARNING:  
                         //      string can grow only UNTAINT_TIMES_BIGGER  
                         switch(to_lang) {  
                         case UL_CLEAN:  
                                 // clean piece  
                                 { // optimizing whitespace  
                                         const char *src=row->item.ptr;   
                                         for(int size=row->item.size; size--; src++)  
                                                 switch(*src) {  
                                                 case ' ': case '\n': case '\t':  
                                                         if(!whitespace) {  
                                                                 *dest++=*src;  
                                                                 whitespace=true;  
                                                         }  
                                                         break;  
                                                 default:  
                                                         whitespace=false;  
                                                         *dest++=*src;  
                                                         break;  
                                                 }  
                                 }  
                                 break;  
                         case UL_TAINTED:  
                                 // tainted piece, but undefined untaint language  
                                 // for VString.as_double of tainted values  
                                 // for ^process{body} evaluation  
                         case UL_AS_IS:  
                                 // tainted, untaint language: as-is  
                                 memcpy(dest, row->item.ptr, row->item.size);   
                                 dest+=row->item.size;  
                                 break;  
                         case UL_FILE_SPEC:  
                                 // tainted, untaint language: file [name]  
                                 escape(switch(*src) {  
                                         case ' ': to_char('_');  break;  
                                         encode(need_file_encode, '+');  
                                 });  
                                 break;  
                         case UL_URI:  
                                 // tainted, untaint language: uri  
                                 escape(switch(*src) {  
                                         case ' ': to_char('+');  break;  
                                         encode(need_uri_encode, '%');  
                                 });  
                                 break;  
                         case UL_HTTP_HEADER:  
                                 // tainted, untaint language: http-field-content-text  
                                 escape(switch(*src) {  
                                         case ' ': to_char('+');  break;  
                                         encode(need_uri_encode, '%');  
                                 });  
                                 break;  
                         case UL_MAIL_HEADER:  
                                 // tainted, untaint language: mail-header  
                                 if(charset) {  
                                         // Subject: Re: parser3: =?koi8-r?Q?=D3=C5=CD=C9=CE=C1=D2?=  
                                         const char *src=row->item.ptr;   
                                         bool to_quoted_printable=false;  
                                         for(int size=row->item.size; size--; src++) {  
                                                 if(*src & 0x80) {  
                                                         if(!to_quoted_printable) {  
                                                                 dest+=sprintf(dest, "=?%.15s?Q?", charset);  
                                                                 to_quoted_printable=true;  
                                                         }  
                                                         dest+=sprintf(dest, "=%02X", *src & 0xFF);  
                                                 } else {  
                                                         *dest++=*src;                                             
                                                 }  
                                         }  
                                         if(to_quoted_printable) // close  
                                                 dest+=sprintf(dest, "?=");  
                                 } else {  
                                         memcpy(dest, row->item.ptr, row->item.size);   
                                         dest+=row->item.size;  
                                 }  
                                 break;  
                         case UL_TABLE:   
                                 // tainted, untaint language: table  
                                 escape(switch(*src) {  
                                         case '\t': to_char(' ');  break;  
                                         case '\n': to_char(' ');  break;  
                                         _default;  
                                 });  
                                 break;  
                         case UL_SQL:  
                                 // tainted, untaint language: sql  
                                 if(connection)  
                                         dest+=connection->quote(dest, row->item.ptr, row->item.size);  
                                 else  
                                         throw Exception(0, 0,  
                                                 this,  
                                                 "untaint in SQL language failed - no connection specified");  
                                 break;  
                         case UL_JS:  
                                 escape(switch(*src) {  
                                         case '"': to_string("\\\"", 2);  break;  
                                         case '\'': to_string("\\'", 2);  break;  
                                         case '\n': to_string("\\n", 2);  break;  
                                         case '\\': to_string("\\\\", 2);  break;  
                                         case '\xFF': to_string("\\\xFF", 2);  break;  
                                         _default;  
                                 });  
                                 break;  
                         case UL_XML:  
                                 escape(switch(*src) {  
                                         case '&': to_string("&amp;", 5);  break;  
                                         case '>': to_string("&gt;", 4);  break;  
                                         case '<': to_string("&lt;", 4);  break;  
                                         case '"': to_string("&quot;", 6);  break;  
                                         case '\'': to_string("&apos;", 6);  break;  
                                         _default;  
                                 });  
                                 break;  
                         case UL_HTML:  
                                 escape(switch(*src) {  
                                         case '&': to_string("&amp;", 5);  break;  
                                         case '>': to_string("&gt;", 4);  break;  
                                         case '<': to_string("&lt;", 4);  break;  
                                         case '"': to_string("&quot;", 6);  break;  
                                         _default;  
                                 });  
                                 break;  
                         case UL_USER_HTML: {  
                                 // tainted, untaint language: html-typo  
                                 if(!typo_dict) // never, always has default  
                                         throw Exception(0, 0,  
                                                 this,  
                                                 "untaint to user-html lang failed, no typo table");  
   
                                 char *html_for_typo=  
                                         (char *)malloc(row->item.size*2/* '\n' -> '\' 'n' */+1,16);  
                                 // note:  
                                 //   there still is a possibility that user   
                                 //   would not replace \n as she supposed to  
                                 //   and rather replace \ and n into huge strings  
                                 //   thus causing memory overrun  
                                 //   this can be dealed by allocating *2 memory, but that's too expensive  
                                 size_t html_for_typo_size;  
                                 { // local dest  
                                         char *dest=html_for_typo;  
                                         escape(switch(*src) {  
                                                 // convinient name for typo match "\n"  
                                                 case '\n':   
                                                         to_string("\\n", 2);  
                                                         break;  
                                                 _default;  
                                         });  
                                         *dest=0;  
                                         html_for_typo_size=dest-html_for_typo;  
                                 }  
                                 // typo table replacements  
                                 const char *src=html_for_typo;  
                                 do {  
                                         // there is a row where first column starts 'src'  
                                         if(Table::Item *item=typo_dict->first_that_starts(src)) {  
                                                 // get a=>b values  
                                                 const String& a=*static_cast<Array *>(item)->get_string(0);  
                                                 const String& b=*static_cast<Array *>(item)->get_string(1);  
                                                 // overflow check:  
                                                 //   b allowed to be max UNTAINT_TIMES_BIGGER then a  
                                                 if(b.size()>UNTAINT_TIMES_BIGGER*a.size()) {  
                                                         pool().set_tag(0); // avoid recursion  
                                                         throw Exception(0, 0,   
                                                                 &b,   
                                                                 "is %g times longer then '%s', "  
                                                                 "while maximum, handled by Parser, is %d",   
                                                                         ((double)b.size())/a.size(),   
                                                                         a.cstr(),   
                                                                         UNTAINT_TIMES_BIGGER);  
                                                 }  
                                                   
                                                 // skip 'a' in 'src'  
                                                 src+=a.size();  
                                                 // write 'b' to 'dest'  
                                                 b.store_to(dest);  
                                                 // skip 'b' in 'dest'  
                                                 dest+=b.size();  
                                         } else  
                                                 *dest++=*src++;  
                                 } while(*src);  
                                 break;  
                                 }  
                         default:  
                                 throw Exception(0, 0,                                   throw Exception(0, 0, 
                                         this,                                           this, 
                                         "unknown untaint language #%d of %d piece",                                           "unknown untaint language #%d", 
                                                 static_cast<int>(row->item.lang),                                                   static_cast<int>(show_lang)); // sould never
                                                 i); // never  
                                 break; // never  
                         }  
   
                         if((lang==UL_UNSPECIFIED?row->item.lang:lang)!=UL_CLEAN)                          dest+=sprintf(dest, "#%s%s: ",
                                 whitespace=false;                                  String_Untaint_lang_name[show_lang],
                                   row->item.lang & UL_OPTIMIZE_BIT?".O":"");
                           char *dest_after_origins=dest;
   
                         if(forigins_mode)                          memcpy(dest, row->item.ptr, row->item.size); 
                                 if(dest==dest_after_origins) // never moved==optimized space                          dest+=row->item.size;
                                         dest=dest_before_origins;  
                                 else {  
                                         remove_crlf(dest_after_origins, dest);  
   
                                         to_char('\n');                          remove_crlf(dest_after_origins, dest);
                                 }                          to_char('\n');
                 }                  }
                 chunk=row->link;                  chunk=row->link;
         } while(chunk);          } while(chunk);
   
 break2:  break2:
         return dest;          *dest=0;
           return result;
 }  }

Removed from v.1.73  
changed lines
  Added in v.1.82


E-mail: