Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[8.0] Only escape callable output of add and edit column. #1852

Merged
merged 12 commits into from
Nov 23, 2018

Conversation

sharifzadesina
Copy link
Contributor

Fixes #1849
P.S. bro I think you need to rewrite whole package again, you use nesting extremely.

@yajra
Copy link
Owner

yajra commented Sep 25, 2018

Thanks, can you please fix the failing tests?

you use nesting extremely

Yeah, will try to refactor more when I got the chance. :)

-- Edit --
I just recalled, I think the nesting was due to support for eager loading.

@sharifzadesina
Copy link
Contributor Author

sharifzadesina commented Sep 25, 2018

@yajra Done.
About "extremely nesting" I mean, "nesting code extremely" in the whole of the project.
for example this function here.
you can combine it with escapeRow and make code cleaner.

@yajra
Copy link
Owner

yajra commented Sep 29, 2018

OIC, thanks for pointing that out. It seems the tests are still failing though.

@sharifzadesina
Copy link
Contributor Author

@yajra I doubt if they are cause of my code.

@yajra
Copy link
Owner

yajra commented Oct 5, 2018

@sharifzadesina ok thanks, will to review this later.

@yajra
Copy link
Owner

yajra commented Oct 5, 2018

Just tested this with my app and it breaks my DT with the same error on the tests. Please fix. Thanks!

exception: "Symfony\Component\Debug\Exception\FatalThrowableError"
file: "/www/yajra/dataTables/core/src/Processors/DataProcessor.php"
line: 241
message: "Argument 1 passed to Yajra\DataTables\Processors\DataProcessor::escapeRow() must be of the type array, integer given, called in /www/yajra/dataTables/core/src/Processors/DataProcessor.php on line 223"

@sharifzadesina
Copy link
Contributor Author

@yajra Bro I fixed the error, and improved the package a lot.
please test the PR and merge it.
this will make the life for developers easier.
also I hope you will work on the package and improve other parts too.
thank you

@yajra
Copy link
Owner

yajra commented Oct 17, 2018

@sharifzadesina thanks but it's failing on the indexColumn. I think it should be the last item on row manipulations as per the original code before PR.


$data = $this->escapeRow(Helper::convertToArray($row));
$value = $this->addColumns($data, $row);
$value = $this->addIndexColumn($value, $row);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this should be the last item in row processing or maybe second to the last then escape the row.

@sharifzadesina
Copy link
Contributor Author

sharifzadesina commented Oct 17, 2018

@yajra Fixed, but it was cause of an other error actually.

@yajra
Copy link
Owner

yajra commented Oct 18, 2018

The changes looks good. However, there are some columns on my project that should be raw but are being escaped. It's not blade but a closure that returns a view.

->rawColumns(['filename_html', 'deleted_html', 'cooked_html']);

image

Need to review this further. I think we are missing some tests on raw columns.

@sharifzadesina
Copy link
Contributor Author

@yajra This should be fixed now.

@yajra
Copy link
Owner

yajra commented Oct 18, 2018

Still not working and I think I found where the issue was:

  1. escapeRow should be last one to be called since added & edited columns are not yet processed.
        foreach ($this->results as $row) {
            $data  = Helper::convertToArray($row);
            $value = $this->addColumns($data, $row);
            $value = $this->editColumns($value, $row);
            $value = $this->setupRowVariables($value, $row);
            $value = $this->selectOnlyNeededColumns($value);
            $value = $this->removeExcessColumns($value);
            $value = $this->addIndexColumn($value);
            $value = $this->escapeRow($value);

            $this->output[] = $object ? $value : $this->flatten($value);
        }
  1. Since our goal is to not escape blade string from add/edit column, we can modify our checker to something like:
    protected function shouldEscapeColumn($key)
    {
        foreach ($this->appendColumns as $column) {
            if ($column['name'] == $key && is_string($column['content'])) {
                return false;
            }
        }

        foreach ($this->editColumns as $column) {
            if ($column['name'] == $key && is_string($column['content'])) {
                return false;
            }
        }

        if ($this->escapeColumns === '*') {
            return ! in_array($key, $this->rawColumns); // escape if is not a raw column
        } elseif (is_array($this->escapeColumns)) {
            return in_array($key, array_diff($this->escapeColumns, $this->rawColumns));
        } else {
            return true;
        }
    }

$value[$indexColumn] = ++$this->start;
}

$data = $this->escapeRow(Helper::convertToArray($row));
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be the last process in order to capture added / edited data.

@@ -125,13 +126,28 @@ public function process($object = false)
protected function addColumns($data, $row)
{
foreach ($this->appendColumns as $key => $value) {
$value['content'] = Helper::compileContent($value['content'], $data, $row);
$value['content'] = Helper::compileContent($value['content'], $data, $row, $this->shouldEscapeColumn($key));
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the escape flag here is not needed since escaping will be done on escapeRow process.

@@ -142,7 +158,7 @@ protected function addColumns($data, $row)
protected function editColumns($data, $row)
{
foreach ($this->editColumns as $key => $value) {
$value['content'] = Helper::compileContent($value['content'], $data, $row);
$value['content'] = Helper::compileContent($value['content'], $data, $row, $this->shouldEscapeColumn($key));
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not needed like on addColumns.

{
if (is_string($content)) {
return static::compileBlade($content, static::getMixedValue($data, $param));
} elseif (is_callable($content)) {
return $content($param);
return $escape ? e($content($param)) : $content($param);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be reverted back to original since all data here is callable.

*/
protected function shouldEscapeColumn($key)
{
if ($this->escapeColumns === '*') {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can add the logic here to figure out if added/edited columns are to be escaped.

        foreach ($this->appendColumns as $column) {
            if ($column['name'] == $key && is_string($column['content'])) {
                return false;
            }
        }

        foreach ($this->editColumns as $column) {
            if ($column['name'] == $key && is_string($column['content'])) {
                return false;
            }
        }

@sharifzadesina
Copy link
Contributor Author

@yajra we are not doing escaping at "escapeRow", cause we don't know the content was a blade string or a callable, we need to escape output of callables, that's why we only escape database data using escapeRow.

@yajra
Copy link
Owner

yajra commented Oct 18, 2018

@sharifzadesina we can identify the content by checking the append columns structure:

foreach ($this->appendColumns as $column) {
            if ($column['name'] == $key && is_string($column['content'])) {
                return false;
            }
        }

that's why we only escape database data

Good point on this one.

@yajra
Copy link
Owner

yajra commented Oct 18, 2018

But I think we should still escape added/edited columns since we can still directly return a database value from a closure.

@sharifzadesina
Copy link
Contributor Author

sharifzadesina commented Oct 18, 2018

@yajra
Imagine I've added this callable using addColumn:

function () {
    return '<b>XSS</b>';
}

The content will be string so we won't escape it. this should not happen.
Can you please send your full code? I think it should work with the changes I made.

@yajra
Copy link
Owner

yajra commented Oct 18, 2018

For closure like that, I would still prefer to be explicit and add it on the rawColumns.

For a straight blade string, It's ok to be raw.

addColumn('xss', function ($model) {
    return '<b>XSS</b>'; // should be added to rawColumns
})

addColumn('xss', function ($model) {
    return $model->xss; // should be added to rawColumns
})

addColumn('xss', 'path.to.view'); // can bypass escape
addColumn('xss', '<a href="#">{{ $xss }}</a>'); // can bypass escape

@yajra
Copy link
Owner

yajra commented Oct 18, 2018

See this gist for my debugged processor. It's not yet optimized though but my project does not seem to break.

@sharifzadesina
Copy link
Contributor Author

sharifzadesina commented Oct 18, 2018

@yajra The problem is if you add it to rawColumns it won't escape and the XSS bug will happen.
We want to escape callables by default so if they wanted to not escape callables they can add it to rawColumns.
Can you send your script?

@yajra
Copy link
Owner

yajra commented Oct 18, 2018

Already created the gist.

Yes, we should by default escape all rows for xss protection unless explicitly set as raw column.That is why I agreed of allowing blade templates to be raw since the argument is we are using {{ }} on our template. It's a different case for closure though.

@sharifzadesina
Copy link
Contributor Author

@yajra no I mean the script you use to test my PR

@yajra
Copy link
Owner

yajra commented Oct 18, 2018

It's just a standard dataTable with multiple addColumns. Anyways, I added it on the gist.

@sharifzadesina
Copy link
Contributor Author

@yajra Bro I tested the PR on my own apps, it works without problem, are you sure you are using whole pull request?
cause there were also some changes at Helper.php file.

@sharifzadesina
Copy link
Contributor Author

@yajra Any opinion on this? Why we don't merge it?

@yajra
Copy link
Owner

yajra commented Nov 7, 2018

It's still failing the last time I checked. For now I think the safest implementation would be:

  1. Allow raw columns on the following condition:
  • String template
->addColumn('extra', 'some string')
->editColumn('extra', 'some string')
  • Blade template
->addColumn('extra', 'path.to.blade')
->editColumn('extra', 'path.to.blade')
  1. Do not allow raw columns on closure:
->addColumn('extra', function($model) {
   return 'html';
})
->rawColumns(['extra'])

@sharifzadesina
Copy link
Contributor Author

@yajra It is exactly like that, that's why I told "this is not a BC".

@yajra
Copy link
Owner

yajra commented Nov 8, 2018

Ok, will try to do an actual test again when I got the chance.

@yajra yajra changed the title Only escape callable output [8.0] Only escape callable output of add and edit column. Nov 23, 2018
@yajra yajra merged commit 040eef2 into yajra:8.0 Nov 23, 2018
@yajra
Copy link
Owner

yajra commented Nov 23, 2018

It seems to work well now but it breaks the index column. Reverting, please resubmit the PR and fix the index column part. Thanks!

@yajra
Copy link
Owner

yajra commented Nov 23, 2018

Or you can alternatively send another PR to fix the index column? Will hold the revert and release of this feature until it's fixed. Thanks!

yajra added a commit that referenced this pull request Nov 23, 2018
yajra added a commit that referenced this pull request Nov 23, 2018
@yajra
Copy link
Owner

yajra commented Nov 23, 2018

Fixed the index column and added some tests. Released on v8.13.0 🚀

If you can, please also submit a PR on docs. Thanks!

@yajra
Copy link
Owner

yajra commented Nov 23, 2018

Resume discussion on #1916.

Repository owner locked as resolved and limited conversation to collaborators Nov 23, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants