镜像站点 · 本页由第三方 GitHub 只读镜像提供,非 GitHub 官方站点,不接受任何登录或凭据输入。前往 github.com
Skip to content

App Security: scan multiple directories with --include-dir - #8739

Merged
jek merged 2 commits into
app-security/gatheringfrom
app-security/include-dir
Oct 5, 2026
Merged

jek merged 2 commits into
app-security/gatheringfrom
app-security/include-dir

Conversation

@jek

@jek jek commented Oct 2, 2026 •

Copy link
Copy Markdown
Contributor

WHY are these changes introduced?

App code often lives outside the directory holding the TOML, such as a backend, a shared library or another repository, and couldn't be scanned.

WHAT is this pull request doing?

Add --include-dir to check. The app directory and each --include-dir are merged by real path (duplicates and nested directories are dropped), each is walked under its own repositories' rules, and paths are reported relative to the app directory, so they may start with ../. The secret scan asks each file's own repository for its Git status, and record accepts ../ paths.

How to manually test your changes?

cd <app directory>
shopify app security check --include-dir ../backend
cd ../backend
shopify app security check --path ../<app directory> --include-dir .

Checklist

  • I've considered possible cross-platform impacts (Mac, Linux, Windows)
  • I've considered possible documentation changes
  • I've considered analytics changes to measure impact
  • The change is user-facing — I've identified the correct bump type (patch for bug fixes · minor for new features · major for breaking changes) and added a changeset with pnpm changeset add

@jek
jek added this pull request to stack #8741 October 2, 2026 12:59
@github-actions github-actions Bot added the no-changelog This PR doesn't include a changeset entry. Is an internal only change not relevant to end users. label Oct 2, 2026
@jek jek changed the title Scan multiple directories with --include-dir App Security: scan multiple directories with --include-dir Oct 2, 2026
@jek
jek force-pushed the app-security/include-dir branch from 1e00e54 to 681002d Compare October 2, 2026 13:35
@jek
jek force-pushed the app-security/include-dir branch from 681002d to a22217c Compare October 2, 2026 14:02
@jek
jek marked this pull request as ready for review October 2, 2026 18:36
@jek
jek requested review from a team as code owners October 2, 2026 18:36
@jek
jek requested a review from jplhomer October 2, 2026 18:38
@github-actions

github-actions Bot commented Oct 2, 2026

Copy link
Copy Markdown
Contributor

Differences in type declarations

We detected differences in the type declarations generated by Typescript for this branch compared to the baseline ('main' branch). Please, review them to ensure they are backward-compatible. Here are some important things to keep in mind:

  • Some seemingly private modules might be re-exported through public modules.
  • If the branch is behind main you might see odd diffs, rebase main into this branch.

New type declarations

We found no new type declarations in this PR

Existing type declarations

packages/cli-kit/dist/public/node/api/app-management.d.ts
@@ -7,7 +7,6 @@ export declare const appManagementAppLogsUrl: (organizationId: string, cursor?:
     status?: string;
     source?: string;
 }) => Promise<string>;
-export declare const appManagementChannelSpecExportUrl: (organizationId: string, appId: string) => Promise<string>;
 export interface RequestOptions {
     requestMode: RequestModeInput;
 }

@jek
jek force-pushed the app-security/include-dir branch from cfadbee to ff94de3 Compare October 2, 2026 21:07
jek added 2 commits October 5, 2026 06:43
App Security can scan code outside the app directory, such as a backend or a
shared library. Each scan directory follows its own repositories' rules, and
paths are relative to the app directory, so they may start with ../.
Gathered files are stored relative to the app directory, and no relative path
reaches another drive or a network share, so those files were silently left
unscanned.
@jek
jek force-pushed the app-security/include-dir branch from ff94de3 to 1adfc17 Compare October 5, 2026 13:46
@github-actions

github-actions Bot commented Oct 5, 2026

Copy link
Copy Markdown
Contributor

⚠️ Potential Breaking Changes Detected

This PR contains changes that may break the existing contract.

@shopify/dev_experience — this PR contains breaking changes that require coordination for the next major release.

🏳️ Removed Flags

The following flags were removed from existing commands:

Command Flag
app:security:check --ignore

@jplhomer jplhomer left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM - we'll work on cleaning up the deterministic checks to support this.

@dmerand dmerand left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Overall change works + LGTM. Per usual, I have a couple of agent suggestions to consider, at your discretion.

].filter((candidate, index, all) => all.findIndex(({directory}) => directory === candidate.directory) === index)

return {
scanDirectories: requested.filter(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggestion: a requested directory can be silently unscanned when an enclosing repository prunes an ancestor.

Example from the source: the app repository ignores vendor/, vendor/sdk is its own repository, and the user passes --include-dir vendor/sdk/src. Merging drops src because it sits inside the app. The outer walk prunes vendor/ by ignore rules. The ignored-warning probe asks the repository that owns vendor/sdk, which reports src as not ignored. So nothing from src is gathered, and no warning or coverage gap says why.

This is a static reading, not yet reproduced at runtime.

If outer-wins is intended, consider warning when a retained walk cannot reach a requested directory because an enclosing repository prunes an ancestor — the current probe only asks the requested directory's own parent. --include-dir vendor/sdk works today, so this can stay a suggestion.

paths: [...new Set(absolutePaths.map((path) => normalizeCliPath(relativePath(appDirectory, path))))].sort(),
ignoredScanDirectories: scanDirectories.filter((_directory, index) => gathered[index]?.ignored),
ignoredScanDirectories,
listingStatus: gathered[0]?.listingStatus ?? (rules.gitFiltering ? 'tracked-only' : 'git-ignore-off'),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggestion: the first directory's listing status explains every secret finding, including files from other directories.

listingStatus comes from gathered[0], but with --include-dir the scan directories can have different statuses. Two cases from the source:

  • The app directory is listed and a second repository's listing fails. An ignored .env gathered from the second directory is then explained as inside a nested git repository, when the real reason was the failed listing.
  • The first directory is failed, so every ignored file anywhere in the scan is explained as a listing failure.

Consider carrying the walk or repository status of the file's own scan directory to scanCommittedSecrets, instead of one global status. This affects explanations and evidence, not detection — no missed-finding case is established.

Static reading only; not reproduced at runtime.

export async function gitStatusFor(appRoot: string, file: string): Promise<GitFileStatus> {
const run = async (args: string[]) => runGit(appRoot, args)
/** Asks the file's own repository, which may differ from the app's: a scan directory can be another repository. */
export async function gitStatusFor(file: Pick<SourceFile, 'path' | 'absolutePath'>): Promise<GitFileStatus> {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggestion: the remediation for a file in another repository assumes the app repository.

For a tracked ../backend/.env, this branch suggests git rm --cached ../backend/.env and adding that path to .gitignore. Git cannot remove a path outside the running repository's work tree, and the app's ignore rules cannot protect a sibling repository's file.

The Git probes now correctly run in the file's own repository (dirname(file.absolutePath)), but the evidence strings still read as app-root commands. Consider rendering the remediation and evidence with the owning repository's directory and the filename relative to it, while keeping the finding location app-relative.

Presentation-only: no execution runs these commands. Static reading; not reproduced at runtime.

@jek
jek added this pull request to the merge queue Oct 5, 2026
Merged via the queue into main with commit cf4471d Oct 5, 2026
33 of 57 checks passed
@jek
jek deleted the app-security/include-dir branch October 5, 2026 18:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

no-changelog This PR doesn't include a changeset entry. Is an internal only change not relevant to end users.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants