how to edit/save text file that contains hash variable

Viewed 48

i'm using a perl script that opens a text file and lets me edit it in a textarea field. the following is an excerpt:

# Database Definition
# --------------------------------------------------------
# Definition of your database. Format is
#       field_name => ['position', 'field_type', 'form-length', 'maxlength', 'not_null', 'default', 'valid_expr']

%def = (
'RecordID'            => [ 0, 'numer',    -1,   255, 1, '',              ''],
'Userid'              => [ 1, 'alpha',    -1,   255, 1, '',              ''],
'Title'               => [ 2, 'alpha',    20,   255, 1, '',              ''],
'Category'            => [ 3, 'alpha',    20,   255, 1, '',              ''],
'Type'                => [ 4, 'alpha',    20,   255, 1, 'D2015',              ''],
'Client'              => [ 5, 'alpha',    20,   255, 0, '',              ''],
'Client_database'     => [ 6, 'alpha',    20,   255, 0, '',              ''],
'Installed'           => [ 7, 'alpha',    20,   255, 0, 'xxxxx',           ''],
'Description'         => [ 8, 'alpha',    0,   1500, 1, '',              ''],
'Called_by'           => [ 9, 'alpha',    20,   255, 0, '',              ''],
'Updated'             => [10, 'timestamp',     -1,   25, 0, &get_timestamp(),       ''],
'Display'             => [11, 'alpha',     -1,     4, 0, 'No',             ''],
'Files'               => [12,'alpha',20,255,0,'',''],
'Followup'            => [13, 'alpha',     -1,     4, 0, 'No',             ''],
'Status_public'       => [14, 'alpha',    20,   255, 0, '',              ''],
'Status_private'      => [15, 'alpha',    20,   255, 0, '',              ''],
'Reserved'           => [16, 'alpha',    -1,     5, 0, 'zzzzz',           '']
);


# Select fields. Field name => 'comma seperated list of drop down options'.
    %select_fields   = ( 
);

# client is both radio and checkbox so checkbox can be used on search form

# Radio fields. Field name => comma seperated list of radio buttons.
 %radio_fields    = ( 
'Type' => 'DBMan,Delicia,Client,D2015',
'Display' => 'Yes,No,Deleted',
'Followup' => 'Yes,No',
'Client' => 'delicia,dreydev,ohs1968,bhccwga,interiorsolutions,danaespa,knue,langford,kudzu,suwaneeday',
'Status_public' => 'n/a,Planning,Coding,Alpha,Beta,Complete',
'Status_private' => 'n/a,Planning,Coding,Alpha,Beta,Complete'
);

# Checkbox fields. Field name => Checkbox value.
    %checkbox_fields = (
    'Category' => 'Configuration,Variable,Flag,Subroutine,Field,FieldType,Problem,TroubleTip,File,Hack,ChangeLog,Other',
    'Client' => 'delicia,dreydev,ohs1968,bhccwga,knue',
    'Installed' => 'delicia,dreydev,ohs1968,bhccwga,knue'
);

#   

# delicia added from autogenerate enhancement
# Textarea fields. Field name => '[columns]x[rows]'.
    %textarea_fields   = (
                        Description => '60x10'
                        );

my problem is when i save the edited file the percent signs are converted into unprintable characters. i need them to stay as-is. an example: i may want to add or remove items in the list of radio fields. my page header contains the following:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">

here's a sample of what the edited file looks like:

#

# Database Definition
# --------------------------------------------------------
# Definition of your database. Format is
#       field_name => ['position', 'field_type', 'form-length', 'maxlength', 'not_null', 'default', 'valid_expr']

Þf = (
'RecordID'            => [ 0, 'numer',    -1,   255, 1, '',              ''],
'Userid'              => [ 1, 'alpha',    -1,   255, 1, '',              ''],
'Title'               => [ 2, 'alpha',    20,   255, 1, '',              ''],

the %def was changed to something unrecognizable so the script can't run.

EDIT: here's the code that reads the file:

open (FILEEDIT, $edit_file_data) or &cgierr ("unable to open: $edit_file_data.\nReason: $!");
 if ($db_use_flock) {
 flock(FILEEDIT, 1)  or &cgierr("unable to get exclusive lock on $edit_file_data.\nReason: $!");
 }

$out = join ("", <FILEEDIT>);

close (FILEEDIT);
... I'M OMITTING THE OTHER PARTS OF HTML PAGE
print qq|
 <TEXTAREA NAME="file_data" COLS="66" ROWS="20" wrap=virtual>$out</TEXTAREA></td></tr>
 <td class="text-center"><input type=submit value="$submit_button" class="btn btn-primary"></td></tr>
 </table></form>
|;

now here's the code that writes the file:

my ($out, $write_file_data);

$write_file_data = $in{'write_file_data'};
$out = $in{'file_data'};
$out =~ s/\r//g;

open (FILEDATA, ">$write_file_data") or &cgierr("unable to open: $write_file_data.\nReason: $!");
 if ($db_use_flock) {
 flock(FILEDATA, 2)  or &cgierr("unable to get exclusive lock on $write_file_data.\nReason: $!");
 }
print FILEDATA $out; # write file.
close FILEDATA;

EDIT I think the problem is here but i don't know what it is

sub parse_form { 
# -------------------------------------------------------- 
 my (%in); 
 my ($buffer, $pair, $name, $value,$s); 
PAIR: foreach $name ($query->param()) { 
@value = $query->param("$name"); 
$value = join '~~', @value; 
$name =~ tr/+/ /; 
$name =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg; 
 
$value =~ tr/+/ /; 
$value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg;
if ($value eq "---") { next PAIR; } 
if ($value eq "http://") { next PAIR; }   # Removes default beginning of URLs
unless ($value) { next PAIR; } 
unless ( length ($value) ) { next PAIR; }
unless ($value =~ /\S+/) { next PAIR; } # 01/29/2017
                               
$in{$name} = $value; 
} 
return %in; 
}

i got this code from a modification i found online years ago; it was supposed to handle uploading files and allow multiple select items; if i use the original script i don't have the problem with % but i need those hacks this is the opening form tag:

<FORM ENCTYPE="multipart/form-data" action="$db_script_url" method="POST">
1 Answers

problem:

Your script double-decodes a form field unnecessarily causing data corruption.

$value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg;

%de is the URL encoding of Þ.

Calling pack on the already decoded form data corrupts it.

solution:

Don't double-decode.

You say this happens for good reason. If that is the case, a minimal workaround might be to only do it on those fields that actually need it. Maybe:

my %is_double_decode_field = map { $_=>1 } qw(
    name_of_field_containing_url
    name_of_another_field_containing_url
    ...
);

sub parse_form { 
    my (%in); 
    my ($buffer, $pair, $name, $value, $s); 
    PAIR: foreach $name ($query->param()) { 

        @value = $query->param("$name"); 
        $value = join '~~', @value; 

        if ( $is_double_decode_field{ $name } ) {
            $name =~ tr/+/ /; 
            $name =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg; 
 
            $value =~ tr/+/ /; 
            $value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg;
        }

        if ($value eq "---") { next PAIR; } 
        if ($value eq "http://") { next PAIR; }   # Removes default beginning of URLs
        unless ($value) { next PAIR; } 
        unless ( length ($value) ) { next PAIR; }
        unless ($value =~ /\S+/) { next PAIR; } # 01/29/2017
                               
        $in{$name} = $value; 
    } 
    return %in; 
}

security concerns:

Consider:

$out = join ("", <FILEEDIT>);
# ...
print qq|
 <TEXTAREA NAME="file_data" ...>$out</TEXTAREA>...
|;

The code you show for loading from file does not sanitise it before injecting into your html form, nor does the code shown for writing it back out seem to do so.

If you load the webpage after submitting with an &amp; somewhere, does the textarea contain &amp; or just &?

If you append:

</textarea><textarea name="file2_data">hello!

does an extra form field show up the next time you load?

The code for retrieving the form data looks unsafe too. Use of param() in list context is a vulnerability. multi_param() is advised. See the CGI documentation.

I expect more issues are lurking.

Related