├── README.md
├── content_scripts
└── content.js
├── h1-templates.zip
├── manifest.json
├── package.json
├── samples
└── xss.md
└── web-ext-artifacts
└── h1_templates-1.0.xpi
/README.md:
--------------------------------------------------------------------------------
1 | # HackerOne Templates
2 |
3 | HackerOne Templates is a browser extension that enhances the reporting experience on HackerOne by providing helpful tools and templates for creating reports.
4 |
5 | ## Installation - Chrome
6 |
7 | 1. Download the latest release of the extension from the [releases page](https://github.com/diego95root/h1-templates/releases).
8 | 2. Extract the downloaded archive.
9 | 3. Open Chrome and navigate to `about:extensions`.
10 | 4. Select the directory to load the extension.
11 |
12 | ## Installation - Firefox
13 |
14 | 1. Install it from the official Mozilla addons store at https://addons.mozilla.org/en-US/firefox/addon/h1-templates/
15 |
16 | ## Usage
17 |
18 | Once installed, the extension will automatically activate on the "New Report" page of any HackerOne program. It provides the following features:
19 |
20 | - A button to upload a report template in Markdown format.
21 | - A dropdown menu with pre-made templates that can be inserted into the report.
22 | - A button to clear the entire report form.
23 |
24 | ## Example report templates
25 |
26 | Sample reports can be found in the `samples/` directory.
27 |
28 | ## Contributing
29 |
30 | Pull requests are welcome! For major changes, please open an issue first to discuss what you would like to change.
31 |
32 | ## License
33 |
34 | This project is licensed under the [MIT](https://choosealicense.com/licenses/mit/) license.
35 |
--------------------------------------------------------------------------------
/content_scripts/content.js:
--------------------------------------------------------------------------------
1 | const observer = new MutationObserver(function (mutations) {
2 | mutations.forEach(function (mutation) {
3 | mutation.addedNodes.forEach(function (node) {
4 | if (node.classList && node.classList.contains('daisy-grid')) {
5 | addReportElement(node);
6 | }
7 | });
8 | });
9 | });
10 | observer.observe(document.documentElement, {
11 | childList: true,
12 | subtree: true,
13 | });
14 |
15 | function createTemplateBtn(report) {
16 | const fileEl = document.createElement('div');
17 | // Make it nice!
18 | fileEl.classList =
19 | 'selected-file daisy-button daisy-button--secondary daisy-button--small daisy-button-- daisy-button--daisys-container delay-0';
20 | fileEl.textContent = report.fileName;
21 | fileEl.style.marginRight = '5px';
22 | fileEl.style.marginBottom = '5px';
23 |
24 | fileEl.addEventListener('click', () => {
25 | fileEl.classList.replace(
26 | 'daisy-button--secondary',
27 | 'daisy-button--primary'
28 | );
29 | const selectedFiles = document.querySelectorAll('.selected-file');
30 | selectedFiles.forEach((selectedFile) => {
31 | if (selectedFile !== fileEl) {
32 | selectedFile.classList.remove('daisy-button--primary');
33 | selectedFile.classList.add('daisy-button--secondary');
34 | }
35 | });
36 |
37 | let parsedReport = parseMarkdown(report.content);
38 |
39 | setKeywordText('', '#report_title');
40 | setKeywordText('', '#report_vulnerability_information');
41 | setKeywordText('', '#report_impact');
42 | parsedReport.forEach((section) => {
43 | if (section.name == 'Impact') {
44 | setKeywordText(section.content.trim(), '#report_impact');
45 | } else if (section.name == 'Title') {
46 | setKeywordText(section.content.trim(), '#report_title');
47 | } else if (section.name == 'Severity') {
48 | document
49 | .querySelector(
50 | `.spec-rating-${section.content.trim().toLowerCase()}`
51 | )
52 | ?.click();
53 | } else {
54 | setKeywordText(
55 | '## ' + section.name + '\n\n' + section.content.trim(),
56 | '#report_vulnerability_information'
57 | );
58 | }
59 | });
60 | });
61 | return fileEl;
62 | }
63 |
64 | function parseMarkdown(content) {
65 | const sections = [];
66 | let currentSection = null;
67 |
68 | content.split('\n').forEach((line) => {
69 | if (line.startsWith('## ')) {
70 | const heading = line.substr(3).trim();
71 | currentSection = { name: heading, content: '' };
72 | sections.push(currentSection);
73 | } else if (currentSection) {
74 | currentSection.content += line + '\n';
75 | }
76 | });
77 |
78 | return sections;
79 | }
80 |
81 | function setKeywordText(text, id) {
82 | var el = document.querySelector(id);
83 | if (el.value === '' || text === '') el.value = text;
84 | else el.value = el.value + '\n\n' + text;
85 | var evt = document.createEvent('Events');
86 | evt.initEvent('change', true, true);
87 | el.dispatchEvent(evt);
88 | }
89 |
90 | function addReportElement(timeline) {
91 | const parentEl = document.querySelector('.daisy-timeline--medium');
92 | const reportList = JSON.parse(localStorage.getItem('reportList'));
93 | reportList?.sort((a, b) => {
94 | if (a.fileName < b.fileName) return -1;
95 | return 1;
96 | });
97 |
98 | const newReportEl = document.createElement('div');
99 | newReportEl.classList.add('daisy-timeline__entry');
100 | newReportEl.innerHTML = `
101 |
102 |
124 | `;
125 |
126 | // Add the new report element to the parent element
127 | parentEl.insertBefore(newReportEl, parentEl.firstChild);
128 |
129 | const selectedFilesContainer = newReportEl.querySelector(
130 | '.selected-files-container'
131 | );
132 | const uploadTemplateBtn = newReportEl.querySelector('.upload-template-btn');
133 |
134 | // Check if reportList exists in local storage and contains at least one report
135 | if (reportList && reportList.length > 0) {
136 | // Display the filenames of loaded reports in the selected-files-container
137 | reportList.forEach((report) => {
138 | const fileEl = createTemplateBtn(report);
139 | selectedFilesContainer.appendChild(fileEl);
140 | });
141 | }
142 |
143 | // Add event listener to uploadTemplateBtn to allow uploading more templates
144 | uploadTemplateBtn.addEventListener('click', () => {
145 | const input = document.createElement('input');
146 | input.type = 'file';
147 | input.accept = '.md';
148 | input.multiple = true;
149 |
150 | input.onchange = () => {
151 | const files = input.files;
152 | for (let i = 0; i < files.length; i++) {
153 | const file = files[i];
154 | const reader = new FileReader();
155 | reader.readAsText(file);
156 | reader.onload = () => {
157 | const content = reader.result;
158 | // Do something with the file content
159 | const report = {
160 | fileName: file.name.replace(/\.md$/, ''),
161 | content: content,
162 | };
163 | // Good that you're reading this! I wouldn't trust anyone with my report templates either :)
164 | // Thankfully they are only stored locally in local storage!
165 | let reportList =
166 | JSON.parse(localStorage.getItem('reportList')) || [];
167 | const existingIndex = reportList.findIndex(
168 | (item) => item.fileName === report.fileName
169 | );
170 |
171 | // If it exists, update the content, otherwise push the new report object to the array
172 | if (existingIndex >= 0) {
173 | reportList[existingIndex].content = report.content;
174 |
175 | // Then replace the button with the new one
176 | const selectedFiles = document.querySelectorAll(
177 | '.selected-file'
178 | );
179 | selectedFiles.forEach((file) => {
180 | if (file.innerText === report.fileName) {
181 | file.remove();
182 | }
183 | });
184 | } else {
185 | reportList.push(report);
186 | }
187 |
188 | // Save the updated reportList to local storage
189 | localStorage.setItem(
190 | 'reportList',
191 | JSON.stringify(reportList)
192 | );
193 |
194 | const fileEl = createTemplateBtn(report);
195 | selectedFilesContainer.appendChild(fileEl);
196 | };
197 | }
198 | };
199 |
200 | input.click();
201 | });
202 | }
203 |
--------------------------------------------------------------------------------
/h1-templates.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/diego95root/h1-templates/8a37fb505fb4ef5bc5d1884636f510583fe6dd0a/h1-templates.zip
--------------------------------------------------------------------------------
/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "manifest_version": 2,
3 | "name": "H1 Templates",
4 | "version": "1.0",
5 | "description": "Augment your H1 report experience by saving report templates in your browser and automatically filling in the reports for you!",
6 | "permissions": [],
7 | "content_scripts": [
8 | {
9 | "matches": ["https://hackerone.com/*"],
10 | "js": ["content_scripts/content.js"],
11 | "run_at": "document_start"
12 | }
13 | ]
14 | }
15 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "h1-templates",
3 | "version": "1.0.0",
4 | "description": "",
5 | "author": "Diego Bernal Adelantado (@godiego)",
6 | "license": "ISC"
7 | }
8 |
--------------------------------------------------------------------------------
/samples/xss.md:
--------------------------------------------------------------------------------
1 | ## Title
2 |
3 | [example.org] DOM XSS via test parameter
4 |
5 | ## Severity
6 |
7 | Medium
8 |
9 | ## Summary:
10 |
11 | Hi team,
12 |
13 | I hope you are well. I found that there is a DOM XSS on https://example.org. This allows attackers to execute arbitrary JS in the context of `example.org` and access all related cookies.
14 |
15 | ## Steps To Reproduce:
16 |
17 | 1. Visit
18 |
19 | {F1811743}
20 |
21 | Kind regards,
22 |
23 | ## Recommended solution
24 |
25 | It is recommended review all input parameters that do not incorporate protection mechanisms
26 |
27 | Properly encode data output. It mainly consists of applying HTML encoding to any output that reproduces data entered by the user to the input, so that it cannot be interpreted as code by the browser. This measure is very important for this type of attacks on the client side.
28 |
29 | Filtering potentially dangerous meta-characters on vulnerable entries. For example, the characters `<`, `>`, `;`, `/` or `\` and all nonprinting characters should be properly filtered at the entry in the application.
30 |
31 | ## Impact
32 |
33 | An attacker that can control the code executed in a victim browser can usually fully compromise this victim. This includes :
34 |
35 | - Perform any action within the application that the user can perform
36 | - Modify any information that the user is able to modify
37 | - Steal user cookies
38 | - Redirect to phishing site
39 | - Denial of service via cookie bomb
40 |
41 |
--------------------------------------------------------------------------------
/web-ext-artifacts/h1_templates-1.0.xpi:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/diego95root/h1-templates/8a37fb505fb4ef5bc5d1884636f510583fe6dd0a/web-ext-artifacts/h1_templates-1.0.xpi
--------------------------------------------------------------------------------