~/tutorials/048-the-scope-is-the-permission.md
048: The scope is the permission
Lesson 045 drew the boundary for an engagement in a signed letter. This tier needs the same discipline one level down, because a web application is not one machine and the question of what you may test does not answer itself. The scope is not the site. The scope is the permission, and on the web it has to be read out of artifacts, not assumed from an address bar.
What this lesson is for
Before the proxy of the next lesson, before any spider or scanner, you must be able to say what you are allowed to point at. Not the host. The exact surface. And you must be able to say where its edges are, because on the web the edges are technical facts that hide in plain sight: other people's machines loaded inside the page you were told to test.
What the reader meets
A page is not a place. It is an assembly. Open any modern site and the conversation from lesson 047, one request, one response, is only the first exchange of many. The page then loads its style, its scripts, its images, its fonts, its analytics, and each of those is another conversation, sometimes with the machine you were told to test, sometimes with six other companies' machines you were told nothing about.
Test the page the way a browser builds it and you are testing all of them. That is the fact this lesson exists to plant, and it is the fact that gets well meaning testers fired.
The census is one command. Ask a page for everything it references and count the origins:
$ curl -s https://l1ackers.com/ | grep -oE '(src|href)="https?://[^"/]*' | sort | uniq -c
Run against this site's home page while writing this lesson, it answers nothing. Every reference is relative, /style.css, /art/cards/site.png, every asset is served by the site itself. That is a design decision, made on purpose, and the census is how you see it as a fact rather than a vibe. Run the same command against any news site and the answer is a column of other companies: script hosts, tag managers, fonts, trackers, each one a machine that is not in your scope no matter what URL you were given.
The instrument
Reading a web scope is reading three artifacts, in order of authority.
The letter, if there is one. A bug bounty program or a client engagement states its scope the way lesson 045 taught: by name, by address, with exclusions. Everything below this line is how you resolve what that letter's names actually mean in the machine. Where there is no letter, there is no test, and that sentence has not moved since the last tier.
The published boundary. Sites state their own rules in files, and two of them are standing offers you read before touching anything. robots.txt, at the root, says what the site asks crawlers to do and not do. This site's, read while writing this lesson:
$ curl -s https://l1ackers.com/robots.txt
# l1ackers.com
User-agent: *
Allow: /
Reading is welcome, with a note asking crawlers to keep the shell endpoint light. That is a published policy in plain lines, the same shape as lesson 045's robots lesson, now aimed at your testing: the site has told you what it considers gentle use, and a tester who ignores a stated preference has been told, in writing, and chose anyway. The second standing file is security.txt from lesson 046, which is where a site tells you where findings go. Neither file grants permission to test. Both files describe the manners of the machine, and manners are evidence of care that you will either match or exploit, on your own head.
The resolver. The letter says example.com. The machine answers a different question: which addresses does that name actually mean, today, now. Two commands:
$ dig +short example.com
$ dig +short www.example.com
Because the letter's names are not the machine's truth. The name may resolve to a CDN, which is a third party's infrastructure, and the letter's fine print or its absence decides whether the edge of the CDN is in or out. The name may have five addresses, and they may belong to three different providers. Scope by name is a claim. Scope by resolution is a fact. You write down both, and where they disagree, that is a question for the scope owner before the first request, not after the first finding.
And one artifact nobody reads that decides half of everything: the page's own third party census from the top of this lesson. The letter authorized the site. The site's pages load other people's machines. The intersection of those two facts is the boundary you actually operate inside, and the census command is how you draw it, per page, before testing.
Try it
- Take one site you own or have written permission to test. Run the origin census on its home page and its busiest page. Write down every third party origin you find. For each one, one sentence: what is it for, and is it in your permission. Any origin you cannot answer for is out of scope by default, and that default is the whole lesson.
- Resolve your target's names.
dig +shortthe bare domain, the www, and any subdomain the letter names. Write down every address and check whether they all belong to the same operator. One lookup service or a whois answers the ownership question, and any address that belongs to someone else is a boundary you now know exists. - Read the target's robots.txt and security.txt before any testing, and save both. They are the machine's own statements about itself, dated, and you will want them later when someone asks what the site said it wanted.
Reveal the answer
The scope worksheet, the one page you write before the first request. This is the shape, filled in for this site as the worked example, every line of it a fact you can check:
TARGET SCOPE WORKSHEET
Authorized surface
Letter or program: (none for the lab, self-owned)
Names authorized: l1ackers.com
Resolved addresses: 172.104.5.114
Address ownership: checked, same operator as the name
Wildcards implied: none stated
Boundaries drawn by census
Third party origins loaded by pages: none, all references relative
CDN or edge in front: Server header says nginx, no CDN header set
Assets served from: the site itself, /art and /style.css same origin
The machine's own statements
robots.txt: Allow /, keep the shell endpoint light
security.txt: absent, noted 2026-09-25, reported to no one yet
Testing manners, decided in advance
Rate: one request at a time, no fuzzing concurrency
Windows: my own hours
Stopped conditions: any sign of another person's traffic in responses
What would end the session immediately
Any third party origin answering from a page
Any address in a response that is not the site's own
Anything the robots note calls heavy, used heavily
Fill one of these out per target and the discipline becomes structural: you cannot unknowingly cross a boundary you wrote down an hour before. The two lines that carry the most weight in practice are the stopped conditions, written while calm, and the rate line, written before the tooling of the later lessons makes concurrency the default you never chose. The worksheet is the letter of lesson 045, translated one level down, and the translation is the skill.
What this lesson does not claim
The worksheet does not make you lawful. It makes you careful, and careful is what the actual permission, the letter, the program terms, or ownership, makes lawful. A perfectly documented test of a machine you had no right to touch is a perfectly documented trespass, and no census command changes that. What this lesson does give you is the inability to cross a boundary by accident, which is the only kind of crossing you were ever at risk of.