Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 18 additions & 4 deletions app/Http/Controllers/Api/ComponentController.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,20 @@

class ComponentController extends AbstractApiController
{
/**
* Parse a comma-separated tags payload into a clean tag list.
*
* @param string $tags
*
* @return array
*/
protected function parseTags($tags)
{
return array_values(array_filter(array_map('trim', preg_split('/ ?, ?/', (string) $tags)), function ($tag) {
Comment on lines +30 to +36
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

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

parseTags() casts $tags to string; if a request submits tags as an array (e.g. tags[]=a&tags[]=b or JSON array), PHP will cast it to the literal string "Array" and this code will create/sync a tag named "Array". Consider explicitly handling arrays (e.g. accept an array of tags) or rejecting non-scalar input with a 400 to avoid silently corrupting tag data.

Suggested change
* @param string $tags
*
* @return array
*/
protected function parseTags($tags)
{
return array_values(array_filter(array_map('trim', preg_split('/ ?, ?/', (string) $tags)), function ($tag) {
* @param string|array $tags
*
* @return array
*/
protected function parseTags($tags)
{
if (is_array($tags)) {
$flattened = [];
foreach ($tags as $tag) {
if (!is_scalar($tag) && $tag !== null) {
throw new BadRequestHttpException('Invalid tags format.');
}
$flattened[] = (string) $tag;
}
$tags = implode(',', $flattened);
} elseif (is_scalar($tags) || $tags === null) {
$tags = (string) $tags;
} else {
throw new BadRequestHttpException('Invalid tags format.');
}
return array_values(array_filter(array_map('trim', preg_split('/ ?, ?/', $tags)), function ($tag) {

Copilot uses AI. Check for mistakes.
return $tag !== '';
}));
}

/**
* Get all components.
*
Expand Down Expand Up @@ -83,9 +97,9 @@ public function postComponents()
throw new BadRequestHttpException();
}

if (Binput::has('tags')) {
if (!is_null(Binput::get('tags', null))) {
// The component was added successfully, so now let's deal with the tags.
$tags = preg_split('/ ?, ?/', Binput::get('tags'));
$tags = $this->parseTags(Binput::get('tags'));

// For every tag, do we need to create it?
$componentTags = array_map(function ($taggable) use ($component) {
Expand Down Expand Up @@ -124,8 +138,8 @@ public function putComponent(Component $component)
throw new BadRequestHttpException();
}

if (Binput::has('tags')) {
$tags = preg_split('/ ?, ?/', Binput::get('tags'));
if (!is_null(Binput::get('tags', null))) {
$tags = $this->parseTags(Binput::get('tags'));

// For every tag, do we need to create it?
$componentTags = array_map(function ($taggable) use ($component) {
Expand Down
18 changes: 16 additions & 2 deletions app/Http/Controllers/Dashboard/ComponentController.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,20 @@ class ComponentController extends Controller
*/
protected $subMenu = [];

/**
* Parse a comma-separated tags payload into a clean tag list.
*
* @param string $tags
*
* @return array
*/
protected function parseTags($tags)
{
Comment on lines +41 to +46
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

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

parseTags() casts $tags to string; if the dashboard form ever submits tags as a non-string (e.g. array due to a UI widget change), PHP will cast arrays to the literal string "Array" and could create an unintended tag named "Array". Consider guarding against non-scalar input (e.g. treat arrays as a list of tags or return an empty list / throw validation error).

Suggested change
* @param string $tags
*
* @return array
*/
protected function parseTags($tags)
{
* @param mixed $tags
*
* @return array
*/
protected function parseTags($tags)
{
// If an array is provided (e.g. from a tags UI component), treat it as a list of tags.
if (is_array($tags)) {
$tagList = [];
foreach ($tags as $tag) {
// Only accept scalar or null-like values; ignore complex structures.
if (!is_scalar($tag) && $tag !== null) {
continue;
}
$value = trim((string) $tag);
if ($value !== '') {
$tagList[] = $value;
}
}
return array_values($tagList);
}
// For non-array, non-scalar inputs, return an empty list instead of creating tags like "Array".
if (!is_scalar($tags) && $tags !== null) {
return [];
}
// Preserve existing behavior for scalar inputs by casting to string and splitting on commas.

Copilot uses AI. Check for mistakes.
return array_values(array_filter(array_map('trim', preg_split('/ ?, ?/', (string) $tags)), function ($tag) {
return $tag !== '';
}));
}

/**
* Creates a new component controller instance.
*
Expand Down Expand Up @@ -145,7 +159,7 @@ public function updateComponentAction(Component $component)
}

// The component was added successfully, so now let's deal with the tags.
$tags = preg_split('/ ?, ?/', $tags);
$tags = $this->parseTags($tags);

// For every tag, do we need to create it?
$componentTags = array_map(function ($taggable) use ($component) {
Expand Down Expand Up @@ -198,7 +212,7 @@ public function createComponentAction()
}

// The component was added successfully, so now let's deal with the tags.
$tags = preg_split('/ ?, ?/', $tags);
$tags = $this->parseTags($tags);

// For every tag, do we need to create it?
$componentTags = array_map(function ($taggable) use ($component) {
Expand Down
Loading