#!/usr/bin/perl
# q(uick)audit.pl: portable src auditing tool -- designed in perl5.  [id: 6931b]
# q(uick)audit.pl: version[02] -- by: vade79[v9@fakehalo.org] :www.fakehalo.org.
#
# this  script/application is for quickly auditing code via .c/.cc source files.
# it checks for standard stack/heap overflows, format bugs, exec calls, env vars
# and misc.  functions related to possible security issues.  this may not always
# be  the  best way to go about auditing, since it is not as good as doing it by
# hand  -  but,  i've  found  it  to  be  rather  useful  for  simple  auditing.
#
# use: "./qaudit.pl"                      ::  for interface qaudit.
# use: "./qaudit.pl <file1> <file2> ..."  ::  for direct qaudit.

# (1/4):presets.
$ansi=1; # change this to 0 if you dont want ansi codes.
@misc_grep=('sprintf','strcpy','strcat','scanf','gets','malloc'); # scan + "(".
@type=('BOF_CHK','FMT_CHK','EXE_CHK','ENV_CHK','MSC_CHK');$prompt="qaudit>";
# (2/4):subroutines.
sub outn{print STDERR "${bld}$srcfile:*NOTICE:${reg}@_";}
sub outb{print STDERR "${bld}$srcfile:$type[0]:".($l+2).":${reg}@_";}
sub outf{print STDERR "${bld}$srcfile:$type[1]:".($l+2).":${reg}@_";}
sub oute{print STDERR "${bld}$srcfile:$type[2]:".($l+2).":${reg}@_";}
sub outev{print STDERR "${bld}$srcfile:$type[3]:".($l+2).":${reg}@_";}
sub outm{print STDERR "${bld}$srcfile:$type[4]:".($l+2).":${reg}@_";}
sub outerror{print STDERR "${bld}ERROR:${reg}@_";}
sub line{print STDERR "${bld}" . "-"x80 . "${reg}\n";}
sub clean_line{
 my $b=0,$tmpstring=shift;
 while((ord(substr($tmpstring,$b,1))==9||ord(substr($tmpstring,$b,1))==32)&&substr($tmpstring,$b,1)){$b++;}
 return substr($tmpstring,$b,(length($tmpstring)-$b));
}
sub bounds_scan{
 if(!@source||!$srcfile){&outerror("${bld}*:${reg} no source file set. (no data)\n");}
 else{
  &outn("${bld}START: entering bounds check mode.${reg}\n");
  undef $chars,$schars,$fc;my @c,@tmpc,$i,$fc,$chars,@schars;$l=0;
  while($source[$l]){
   if($source[$l]=~"char"&&$source[$l]=~"\\["&&$source[$l]=~"\\]"){
    $i=0;@c=split(/\[/,$source[$l]);
    while($c[$i]){
     @tmpc=split(/ /,$c[$i]);
     $fc=$tmpc[($tmpc-1)];$fc=~s/\[//g;$fc=~s/\]//g;$fc=~s/\(//g;
     $fc=~s/\)//g;$fc=~s/\{//g;$fc=~s/\}//g;$fc=~s/\*//g;$fc=~s/\|//g;
     $fc=~s/\&//g;$fc=~s/\t//g;$fc=~s/\r//g;$fc=~s/\n//g;
     if($tmpc[($tmpc-1)]eq$fc){
      if(!($chars=~$fc)){
       if($chars){$chars="$chars $fc";}
       else{$chars=$fc;}
      }
     }
     $i++;
    }
   }
   $l++;
  }
  if($chars){&outn("${bld}INFO: character array(s) to scan: $chars.${reg}\n");}
  @schars=split(/ /,$chars);$i=0;
  while($schars[$i]){
   $l=0;while($source[$l]){
    if($source[$l]=~$schars[$i]){&outb("${bld}$schars[$i]:${reg} ".&clean_line($source[$l]));}
    $l++;
   }
   $i++;
  }
  &outn("${bld}STOP: exiting bounds check mode.${reg}\n");
 }
}
sub format_scan{
 if(!@source||!$srcfile){&outerror("${bld}*:${reg} no source file set. (no data)\n");}
 else{
  &outn("${bld}START: entering format bug check mode.${reg}\n");
  $l=0;while($source[$l]){
   if(($source[$l]=~"printf\\("&&!($source[$l]=~"\"")&&!($source[$l]=~"\'")&&!($source[$l]=~"sprintf"&&!($source[$l]=~"nprintf")))&&$source[$l]=~"\\("){
    &outf("${bld}WARNING:${reg} ".&clean_line($source[$l]));
   }
   elsif(($source[$l]=~"vprintf"||$source[$l]=~"vsprintf"||$source[$l]=~"vfprintf")&&$source[$l]=~"\\("){
    &outf("${bld}WARNING:${reg} ".&clean_line($source[$l]));
   }
   $l++;
  }
  &outn("${bld}STOP: exiting format bug check mode.${reg}\n");
 }
}
sub exec_scan{
 if(!@source||!$srcfile){&outerror("${bld}*:${reg} no source file set. (no data)\n");}
 else{
  &outn("${bld}START: entering exec check mode.${reg}\n");
  $l=0;while($source[$l]){
   if(($source[$l]=~"system"||$source[$l]=~"exec"||$source[$l]=~"popen")&&$source[$l]=~"\\("){
    &oute("${bld}WARNING:${reg} ".&clean_line($source[$l]));
   }
   $l++;
  }
  &outn("${bld}STOP: exiting exec check mode.${reg}\n");
 }
}
sub env_scan{
 if(!@source||!$srcfile){&outerror("${bld}*:${reg} no source file set. (no data)\n");}
 else{
  &outn("${bld}START: entering environmental variable check mode.${reg}\n");
  $l=0;while($source[$l]){
   if(($source[$l]=~"getenv"||$source[$l]=~"setenv"||$source[$l]=~"putenv")&&$source[$l]=~"\\("){
    &outev("${bld}WARNING:${reg} ".&clean_line($source[$l]));
   }
   $l++;
  }
  &outn("${bld}STOP: exiting environmental variable check mode.${reg}\n");
 }
}
sub misc_scan{
 if(!@source||!$srcfile){&outerror("${bld}*:${reg} no source file set. (no data)\n");}
 else{
  &outn("${bld}START: entering miscellaneous check mode.${reg}\n");
  my $k;$l=0;while($source[$l]){
   $k=0;while($misc_grep[$k]){
    if($source[$l]=~$misc_grep[$k]&&$source[$l]=~"\\("){&outm("${bld}WARNING:${reg} ".&clean_line($source[$l]));}
    $k++;
   }
   $l++;
  }
  &outn("${bld}STOP: exiting miscellaneous check mode.${reg}\n");
 }
}
sub read_file{
 my $tmpsrcfile;$srcfile=shift;if(!-f$srcfile){undef $srcfile;}
 open(SOURCE,$srcfile);$sc=<SOURCE>;@source=<SOURCE>;
 close(SOURCE);@tmpsrcfile=split(/\//,$srcfile);
 if($tmpsrcfile[($tmpsrcfile)-1]){$srcfile=$tmpsrcfile[($tmpsrcfile)-1];}
}
sub audit_file{
 &read_file(shift);&line();&format_scan();&bounds_scan();
 &exec_scan();&env_scan();&misc_scan();&line();
}
sub new_file{
 my $tmpsrcfile=shift;undef $srcfile,@source;
 if(substr($tmpsrcfile,(length($tmpsrcfile)-2),2)ne".c" &&substr($tmpsrcfile,(length($tmpsrcfile)-3),3)ne".cc"){print "${bld}invalid source file: $tmpsrcfile.${reg}\n";}
 elsif(!-f$tmpsrcfile){&outerror("${bld}*:${reg} no such file: $tmpsrcfile.\n");}
 else{&audit_file($tmpsrcfile);}
}
# (3/4):init.
if($ansi){$bld="\x1b[1;37m";$reg="\x1b[0m";}
printf "${bld}qaudit.pl::q\(uick\)audit: version[02].  by: vade79[v9\@fakehalo.org].${reg}\n";
&line();sleep(1);
if(!$ARGV[0]){
 my $input,@trunc,$tmpsrcfile;
 while(1){
  print STDERR "${bld}$prompt${reg} ";
  $input=<STDIN>;chomp $input;
  @trunc=split(/ /,$input);
  if(substr($trunc[0],0,1)eq"f"){
   if(!$trunc[1]){print "${bld}file syntax: file <filename>.${reg}\n";}
   else{
    $tmpsrcfile=$trunc[1];
    if(substr($tmpsrcfile,(length($tmpsrcfile)-2),2)ne".c" &&substr($tmpsrcfile,(length($tmpsrcfile)-3),3)ne".cc"){print "${bld}invalid source file: $tmpsrcfile.${reg}\n";undef $tmpsrcfile;}
    elsif(-f$tmpsrcfile){print "${bld}source file set to: $tmpsrcfile.${reg}\n";}
    else{print "${bld}no such file: $tmpsrcfile.${reg}\n";undef $tmpsrcfile;}
   }
  }
  elsif(substr($trunc[0],0,1)eq"a"){
   if(-f$tmpsrcfile){
    print "${bld}executing audit on: $tmpsrcfile${reg}\n";
    &new_file($tmpsrcfile);
   }
   else{print "${bld}no file has been selected to audit, use \"file\".${reg}\n";}
  }
  elsif(substr($trunc[0],0,1)eq"e"){print "${bld}exiting qaudit.${reg}\n";exit(0);}
  else{print "${bld}interface commands: file audit exit.${reg}\n";}
 }
}
else{
 $a=0;while($ARGV[$a]){&new_file($ARGV[$a]);$a++;}
}
print "${bld}exiting qaudit.${reg}\n";exit(0);
# (4/4):eof.
