This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include
, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | |||
3 | namespace Audit\Traits; |
||
4 | |||
5 | // Deps |
||
6 | use App; |
||
7 | use Facilitador\Models\Admin; |
||
8 | use Audit\Models\Change; |
||
9 | use MediaManager\Models\Image; |
||
10 | use Facilitador; |
||
11 | use Illuminate\Database\Eloquent\Builder; |
||
12 | use Illuminate\Database\Eloquent\SoftDeletingScope; |
||
13 | use Muleta\Traits\Models\SerializeWithImages; |
||
14 | use Muleta\Traits\Models\CanSerializeTransform; |
||
15 | |||
16 | /** |
||
17 | * Enable logging changes to models |
||
18 | */ |
||
19 | trait Loggable |
||
20 | { |
||
21 | /** |
||
22 | * The name of the scope that is applied to make trashed versions to be |
||
23 | * viewable |
||
24 | * |
||
25 | * @var string |
||
26 | */ |
||
27 | static public $LOGGABLE_SCOPE = 'view trashed versions'; |
||
28 | |||
29 | /** |
||
30 | * Previous (to the change referenced in the request query) changes made |
||
31 | * to this model |
||
32 | * |
||
33 | * @var Collection |
||
34 | */ |
||
35 | private $previous_changes; |
||
36 | |||
37 | /** |
||
38 | * Get the polymorphic relationship to Changes |
||
39 | * |
||
40 | * @return Illuminate\Database\Eloquent\Relations\Relation |
||
41 | */ |
||
42 | public function changes() |
||
43 | { |
||
44 | return $this->morphMany(Change::class, 'loggable', 'model', 'key'); |
||
0 ignored issues
–
show
|
|||
45 | } |
||
46 | |||
47 | /** |
||
48 | * Boot events |
||
49 | * |
||
50 | * @return void |
||
51 | */ |
||
52 | public static function bootLoggable() |
||
53 | { |
||
54 | // Add scope that will fetch trashed versions of models when the |
||
55 | // Change::QUERY_KEY is present. |
||
56 | static::addGlobalScope( |
||
57 | static::$LOGGABLE_SCOPE, function (Builder $builder) { |
||
58 | static::showTrashedVersion($builder); |
||
59 | } |
||
60 | ); |
||
61 | |||
62 | // Substitute attribute values from changes on load |
||
63 | static::retrieved( |
||
64 | function ($model) { |
||
65 | $model->replaceAttributesWithChange(); |
||
66 | } |
||
67 | ); |
||
68 | } |
||
69 | |||
70 | /** |
||
71 | * Should this model log it's changes. Defaults to true if the change |
||
72 | * happened while handling an admin request or via the console but not |
||
73 | * during a non-http unit test. |
||
74 | * |
||
75 | * @param string $action Like "deleted", "updated", etc |
||
76 | * @return boolean |
||
77 | */ |
||
78 | public function shouldLogChange($action) |
||
0 ignored issues
–
show
|
|||
79 | { |
||
80 | return Facilitador::handling() |
||
81 | || (App::runningInConsole() && request()->path() == '/'); |
||
82 | } |
||
83 | |||
84 | /** |
||
85 | * Show trashed models for matching change |
||
86 | * |
||
87 | * @param Builder $builder |
||
88 | * @return void |
||
89 | */ |
||
90 | private static function showTrashedVersion(Builder $builder) |
||
91 | { |
||
92 | if (($change = static::lookupRequestedChange()) |
||
93 | && static::builderMatchesChange($change, $builder) |
||
94 | ) { |
||
95 | $builder->withoutGlobalScope(SoftDeletingScope::class); |
||
96 | } |
||
97 | } |
||
98 | |||
99 | /** |
||
100 | * Get a Change record mentioned in the query, if appropriate |
||
101 | * |
||
102 | * @return Change|void |
||
103 | */ |
||
104 | private static function lookupRequestedChange() |
||
105 | { |
||
106 | // Only run if the query param is present |
||
107 | if (!$change_id = request(Change::QUERY_KEY)) { |
||
108 | return; |
||
109 | } |
||
110 | |||
111 | // Don't execute for classes that result in recusirve queries when the |
||
112 | // Change model gets built below |
||
113 | $class = get_called_class(); |
||
114 | if (in_array($class, [Change::class, Admin::class, Image::class])) { |
||
115 | return; |
||
116 | } |
||
117 | |||
118 | // Check whether the referenced Change is for this class |
||
119 | $change = Change::find($change_id); |
||
120 | if ($class == $change->model) { |
||
121 | return $change; |
||
122 | } |
||
123 | } |
||
124 | |||
125 | /** |
||
126 | * Does the Change referenced in the GET query match the conditions already |
||
127 | * applied in the query builder? |
||
128 | * |
||
129 | * @param Change $change |
||
130 | * @param Builder $builder |
||
131 | * @return boolean |
||
132 | */ |
||
133 | private static function builderMatchesChange(Change $change, Builder $builder) |
||
134 | { |
||
135 | $class = $change->model; |
||
0 ignored issues
–
show
The property
model does not exist on object<Audit\Models\Change> . Since you implemented __set , maybe consider adding a @property annotation.
Since your code implements the magic setter <?php
/**
* @property int $x
* @property int $y
* @property string $text
*/
class MyLabel
{
private $properties;
private $allowedProperties = array('x', 'y', 'text');
public function __get($name)
{
if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
return $properties[$name];
} else {
return null;
}
}
public function __set($name, $value)
{
if (in_array($name, $this->allowedProperties)) {
$properties[$name] = $value;
} else {
throw new \LogicException("Property $name is not defined.");
}
}
}
Since the property has write access only, you can use the @property-write annotation instead. Of course, you may also just have mistyped another name, in which case you should fix the error. See also the PhpDoc documentation for @property. ![]() |
|||
136 | $route_key_name = (new $class)->getRouteKeyName(); |
||
137 | return collect($builder->getQuery()->wheres) |
||
138 | ->contains( |
||
139 | function ($where) use ($change, $route_key_name) { |
||
140 | |||
141 | // If the builder is keyed to a simple "id" in the route, return |
||
142 | // whether the Change matches it. |
||
143 | if ($route_key_name == 'id') { |
||
144 | return $where['column'] == $route_key_name |
||
145 | && $where['operator'] == '=' |
||
146 | && $where['value'] == $change->key; |
||
0 ignored issues
–
show
The property
key does not exist on object<Audit\Models\Change> . Since you implemented __get , maybe consider adding a @property annotation.
Since your code implements the magic getter <?php
/**
* @property int $x
* @property int $y
* @property string $text
*/
class MyLabel
{
private $properties;
private $allowedProperties = array('x', 'y', 'text');
public function __get($name)
{
if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
return $properties[$name];
} else {
return null;
}
}
public function __set($name, $value)
{
if (in_array($name, $this->allowedProperties)) {
$properties[$name] = $value;
} else {
throw new \LogicException("Property $name is not defined.");
}
}
}
If the property has read access only, you can use the @property-read annotation instead. Of course, you may also just have mistyped another name, in which case you should fix the error. See also the PhpDoc documentation for @property. ![]() |
|||
147 | |||
148 | // Otherwise compare against model logged by the change. The |
||
149 | // scope needs to be removed to prevent recursion. |
||
150 | } else { |
||
151 | $value = $change->changedModel() |
||
152 | ->withoutGlobalScope(static::$LOGGABLE_SCOPE) |
||
153 | ->first() |
||
154 | ->$route_key_name; |
||
155 | return $where['column'] == $route_key_name |
||
156 | && $where['operator'] == '=' |
||
157 | && $where['value'] == $value; |
||
158 | } |
||
159 | } |
||
160 | ); |
||
161 | } |
||
162 | |||
163 | /** |
||
164 | * Replace all the attributes with those from the specified Change specified |
||
165 | * in the reqeust query. |
||
166 | * |
||
167 | * @return void |
||
168 | */ |
||
169 | private function replaceAttributesWithChange() |
||
0 ignored issues
–
show
|
|||
170 | { |
||
171 | if (($change = static::lookupRequestedChange()) |
||
172 | && $change->key == $this->getKey() |
||
0 ignored issues
–
show
It seems like
getKey() must be provided by classes using this trait. How about adding it as abstract method to this trait?
This check looks for methods that are used by a trait but not required by it. To illustrate, let’s look at the following code example trait Idable {
public function equalIds(Idable $other) {
return $this->getId() === $other->getId();
}
}
The trait Adding the ![]() The property
key does not exist on object<Audit\Models\Change> . Since you implemented __get , maybe consider adding a @property annotation.
Since your code implements the magic getter <?php
/**
* @property int $x
* @property int $y
* @property string $text
*/
class MyLabel
{
private $properties;
private $allowedProperties = array('x', 'y', 'text');
public function __get($name)
{
if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
return $properties[$name];
} else {
return null;
}
}
public function __set($name, $value)
{
if (in_array($name, $this->allowedProperties)) {
$properties[$name] = $value;
} else {
throw new \LogicException("Property $name is not defined.");
}
}
}
If the property has read access only, you can use the @property-read annotation instead. Of course, you may also just have mistyped another name, in which case you should fix the error. See also the PhpDoc documentation for @property. ![]() |
|||
173 | ) { |
||
174 | $this->attributes = array_merge( |
||
0 ignored issues
–
show
The property
attributes does not exist. Did you maybe forget to declare it?
In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code: class MyClass { }
$x = new MyClass();
$x->foo = true;
Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion: class MyClass {
public $foo;
}
$x = new MyClass();
$x->foo = true;
![]() |
|||
175 | $this->attributes, |
||
176 | $this->attributesAtChange($change) |
||
177 | ); |
||
178 | } |
||
179 | } |
||
180 | |||
181 | /** |
||
182 | * Get the attributes of the model at a given Change |
||
183 | * |
||
184 | * @param Change $change |
||
185 | * @return array |
||
186 | */ |
||
187 | private function attributesAtChange(Change $change) |
||
188 | { |
||
189 | return $this->previousChanges($change) |
||
190 | ->reduce( |
||
191 | function ($attributes, $change) { |
||
192 | return array_merge($attributes, $change->changed); |
||
193 | }, [] |
||
194 | ); |
||
195 | } |
||
196 | |||
197 | /** |
||
198 | * Get the list of pervious changes of this model, storing it to reduce |
||
199 | * future lookups |
||
200 | * |
||
201 | * @param Change $change |
||
202 | * @return Collection |
||
203 | */ |
||
204 | private function previousChanges(Change $change) |
||
205 | { |
||
206 | return $this->changes() |
||
207 | ->where('changes.id', '<=', $change->id) |
||
0 ignored issues
–
show
The property
id does not exist on object<Audit\Models\Change> . Since you implemented __get , maybe consider adding a @property annotation.
Since your code implements the magic getter <?php
/**
* @property int $x
* @property int $y
* @property string $text
*/
class MyLabel
{
private $properties;
private $allowedProperties = array('x', 'y', 'text');
public function __get($name)
{
if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
return $properties[$name];
} else {
return null;
}
}
public function __set($name, $value)
{
if (in_array($name, $this->allowedProperties)) {
$properties[$name] = $value;
} else {
throw new \LogicException("Property $name is not defined.");
}
}
}
If the property has read access only, you can use the @property-read annotation instead. Of course, you may also just have mistyped another name, in which case you should fix the error. See also the PhpDoc documentation for @property. ![]() |
|||
208 | ->orderBy('changes.id', 'asc') |
||
209 | ->get(); |
||
210 | |||
211 | } |
||
212 | |||
213 | } |
||
214 |
This check looks for methods that are used by a trait but not required by it.
To illustrate, let’s look at the following code example
The trait
Idable
provides a methodequalsId
that in turn relies on the methodgetId()
. If this method does not exist on a class mixing in this trait, the method will fail.Adding the
getId()
as an abstract method to the trait will make sure it is available.