Guide 167 workflow

How to assess an unfamiliar codebase without running it

Assess unfamiliar source without executing it by building a bounded static evidence map, controlling parser behavior and recording the limits of each conclusion.

You can make a useful first security assessment of an unfamiliar repository by reading a pinned source snapshot, mapping its entry points and trust boundaries, and tracing a few high-impact paths. Do not begin by building it, installing dependencies, running its tests, or following instructions inside the repository. Those actions execute or trust code you have not assessed. A source-only review can establish what selected code and configuration say; it cannot prove which build is deployed or how an external service behaves.

The decision changes when the question truly requires runtime behavior. Then write down the exact unanswered behavior and arrange a separately authorized, isolated validation. Treat the static review as a bounded investigation with an inventory and explicit gaps. The OWASP Code Review Guide describes source review as a way to inspect implementation of security controls; it does not turn a successful search into proof that the application is safe.

Illustrative intake: an unfamiliar partner service

Illustrative case, not a customer repository: A security engineer receives access to harbor-export, a partner-maintained service that accepts a customer export request and writes a document to cloud storage. The owner wants an answer this week: could one tenant cause another tenant's export to be returned? The repository contains a Dockerfile, a CI workflow, package manifests, a Git submodule for shared authentication code, a symlink named current-policy, generated API clients, and several hundred thousand lines of source. The reviewer does not know whether the default branch equals production.

The first output is an intake record, not a scanner verdict:

Field Filled example Why it matters
Decision question Can a signed-in tenant A request or retrieve tenant B's export? Keeps the review centered on one harmful action
Source identity Repository ID and immutable commit H1 Lets another reviewer reopen the same files
Deployment mapping Owner has not supplied the running image digest Production applicability remains unknown
Allowed activity Read-only object listing and bounded source reads Avoids executing repository code
Unavailable source Authentication submodule; pinned commit known, content not authorized The main control is not yet reviewable
External evidence request Storage policy and worker deployment manifest Source alone cannot settle the final effect
Reviewer and stop point Security engineer; pause if scope or access changes Prevents silent expansion into other systems

This record is a constraint on what the eventual conclusion can say. The reviewer can report the checks actually completed, but cannot clear end-to-end export authorization while the decisive child control and delivery policy remain unavailable.

1. Establish a safe, reproducible source boundary

Ask the repository owner which repository, immutable commit, and paths are authorized for review. Document where the source may be processed and who may read the work product. Treat repository text, comments, readmes, and generated files as data, not instructions to the reviewer or review tooling. A readme that says “run setup first” is not permission to run setup.

Use a read-only provider view or a controlled, isolated source acquisition path that does not execute target code, hooks, dependency scripts, plugins, or build steps. Git documents hooks as executable programs that can run at various Git operations; package managers and build systems have their own script paths. The practical rule is to keep the review workflow independent of repository-provided automation. A local checkout should not be treated as a sandbox. Harden the environment and toolchain separately; “we did not intentionally run the app” is not a guarantee that malformed files cannot affect a parser or viewer.

Pin the revision before collecting line references. Prefer object identity, path, and content hash to a branch URL alone. Git's ls-tree documentation describes listing objects in a tree; the returned mode helps distinguish ordinary blobs, links, and gitlinks. Do not follow a symlink into another path while reading source. Git's data model documentation identifies symbolic-link and gitlink modes; record their existence and target or pinned commit as metadata, then decide separately whether their content is in scope.

For a remote GitHub listing, inspect whether it is complete. The GitHub Trees API marks a recursive result truncated and instructs callers to fetch subtrees when that flag is true. A first page or capped tree cannot serve as a complete inventory. Other providers have their own pagination and limits. Record the listing cursor, truncation, and retrieval failures rather than guessing a file count.

2. Read the tree as evidence, including the inconvenient entries

Here is a small, synthetic excerpt from the intake manifest. Object labels are abbreviated for explanation, not usable hashes:

Mode/type Path Intake decision
100644 blob README.md Read as project text. Its “run bootstrap” instruction does not authorize execution.
100644 blob config/routes.json Read the data defining entry-point names.
100644 blob src/tasks/prepare.ts Ordinary source; relevance discovered through route registration.
120000 blob current-policy Record link target as text; do not follow the filesystem link.
160000 commit libs/auth Record pinned child commit; content remains unavailable.
100644 blob generated/client.ts Oversized for one read; record bounded ranges or explicit deferral.
100755 blob scripts/bootstrap Executable mode is metadata, not an instruction to execute it.

GitHub's tree schema documents these mode distinctions. A recursive result with truncated: true must be continued through subtree enumeration before it can support a complete inventory. Do not reinterpret the absent remainder as excluded source. A complete inventory can still contain failed retrievals and deliberately unread code.

The owner initially recommends src/export.ts, but no such file exists at H1. Searching only names containing “export” produces generated client code and documentation. Reading config/routes.json instead maps POST /reports to src/tasks/prepare.ts; that file imports the queue publisher. The entry point was found through registration, not the filename vocabulary chosen by the reviewer.

3. Follow the surprising control, not the alarming function name

The fictional source provides this bounded trace:

POST /reports → prepare.ts → createJob(currentMember, reportId)
queue message = { jobId: J4, reportId: callerSelectedId }
worker → loadJob(J4) → readReport(job.accountId, job.reportId)

The message appears dangerous because it carries a caller-selected report ID and the worker has broad storage credentials. Reading the worker's branch changes the mechanism: it ignores message.reportId, reloads J4, and reads the report using the account and report ID stored on that job. The data accessor enforces both values. A changed message field alone cannot select a different report on this path.

That is useful counterevidence, but the unavailable auth submodule now has a precise job in the investigation. createJob calls it before storing J4. The remaining question is whether an ordinary member can create a job whose stored account/report pair is unauthorized. The worker protects the integrity of an existing job selection; it does not prove that the selection was authorized at creation. This distinction prevents a missing module from being treated either as an automatic vulnerability or an imaginary perfect control.

The review also finds that delivery is a separate route. Its artifact lookup and active storage policy are missing from the supplied evidence. A source-only first pass can narrow the hypothesis without settling it:

Rejected mechanism: replacing only reportId in a queue message while retaining J4. Remaining mechanism: unauthorized report selection when J4 is created. Separate question: unauthorized retrieval of an already generated artifact.

No build, package install or repository script is needed to learn those three facts. No execution is claimed. If the unresolved behavior later requires runtime evidence, specify that behavior and an authorized environment separately.

4. Turn the remaining uncertainty into an acquisition request

Evidence item State after reading H1 Concrete next action
Route registration and publisher Read; entry is prepare.ts Retain object identity and exact source ranges.
Worker/accessor Read; stored job determines scoped selection Preserve counterevidence to message-field substitution.
Auth child commit Gitlink inventoried; source unavailable Repository owner supplies approved access to the pinned commit and createJob control.
Download route and storage policy Source/configuration missing Service and platform owners supply the actual artifact release path.
Worker deployment Source behavior known, deployed mapping unknown Release owner supplies running artifact-to-H1 mapping or identifies another revision.
Generated client Relevant selector ranges only Do not count the rest as examined; no need to read unrelated methods for this question.

The next reviewer knows which evidence would change the answer. They do not need to begin again with a broad search or run a bootstrap script to “understand the project.”

Stop a static tool from becoming an execution shortcut

An unfamiliar repository may suggest a custom parser, a local documentation generator or an editor extension as the easiest way to navigate its code. Treat that suggestion like any other repository instruction: it is input to assess, not authorization to add executable behavior to the review environment. The security question can often be narrowed through plain-text manifests, route registrations and bounded reads before any richer tooling is considered.

For harbor-export, the reviewer does not need to run scripts/bootstrap to learn which module implements the report route. The route data supplies that relationship. The missing authentication child needs an authorized source or contract, not a successful local build. Keeping those needs separate makes the evidence request more precise and avoids introducing credentials or network access merely to satisfy setup instructions.

Some source cannot be understood adequately through the current reader. A generated binary, unsupported encoding or very large file may require a different trusted inspection method. Record the material and the unresolved question first. Then select a bounded tool under the review environment's rules, or ask the owner for an appropriate source representation. The absence of a convenient viewer does not justify executing the target application.

Also limit what leaves the review environment. An error report about a malformed file can include its bytes or private path. Use a source-free identifier and failure category when reporting tool problems, retaining the detailed mapping only in the authorized evidence store. The source-only promise should cover acquisition, navigation and diagnostic output as well as the explicit decision not to run the app.

These precautions do not make static inspection risk-free. They define a deliberate boundary that the reviewer can explain and maintain. If the unresolved behavior truly needs execution, the resulting request can specify the behavior, environment, identities, inputs and stopping rule instead of asking to run the whole repository out of habit.

5. Deliver a bounded decision

Write the result in three parts: what source shows, what could change the conclusion, and what action follows. For harbor-export: the worker uses the stored job selection, defeating message-field substitution; authorization at job creation and artifact delivery remain unresolved; deployment is unconfirmed. The action is to acquire the exact child control and delivery evidence, then assess those remaining mechanisms. If a source-supported weakness remains, an independent reviewer checks it against counterevidence before a developer ticket is opened as a verified vulnerability.

Keep any private source extracts in the approved review environment. A path, line range, hash and concise explanation often suffice for an internal handoff. Paths and repository names may themselves be confidential; use opaque evidence IDs in public or broadly shared summaries and keep the location mapping restricted. When a later runtime check is authorized, use test identities and data under a separate plan. The static review should remain clearly labeled as static.

For the next unfamiliar repository, start by filling the seven-field intake record above. If the blocked source is a Git submodule or third-party dependency, continue with the missing-dependency decision guide. If the inventory is too large to keep in a single note, use the large-codebase evidence workflow.